Forum: Mikrocontroller und Digitale Elektronik strlen-Funktion Funktionsweise


von Frank (Gast)


Lesenswert?

1
size_t strlen(const char * str)
2
   {
3
     const char *s;
4
     for (s = str; *s; ++s)
5
      {}
6
     return(s - str);
7
   }

Hallo zusammen,

kann mir ggf. jmd von euch die Funktionsweise der o.g Funktion erklären 
?
Ich verstehe den Funktionskopf der for-Schleife nicht. Das return auch 
nicht.

Über Hilfe wäre ich sehr dankbar.

Grüße

Frank

von Adam P. (adamap)


Lesenswert?

Frank schrieb:
> s = str;

Der Zeiger s wird auf das erste Zeichen des übergebenen Zeichenkette 
gelegt.

Frank schrieb:
> *s;

Druchlaufe die for() solange wie der Zeiger s wahr ist, also ungleich 0 
bzw. NULL - da Zeichenketten mit '\0' abgeschlossen sein müssen.

Frank schrieb:
> ++s

Falls s noch ungleich 0, dann weiter mit nächstem Zeichen.

Frank schrieb:
> return(s - str)

Irgendwann ist s gleich 0 und es wird die Länge zurückgegeben, diese 
errechnet sich aus der Ende-Adresse minus der Start-Adresse (im RAM).

Zum Bsp. liegt die Zeichenkette str = "Hallo\0" auf der Adresse
0x1000
das \0 Byte befindet sich somit auf Adresse 0x1005.
0x1005 - 0x1000 = 0x0005 -> Die Länge von "Hallo" beträgt 5 Zeichen.

: Bearbeitet durch User
von Einer K. (Gast)


Lesenswert?

Es wird, die so genannte, "Pointer Arithmetik" verwendet.
Google, oder seine Brüder, sollten dazu was finden.
Auch gibt jedes C oder C++ Buch gerne dazu Auskunft.

von Marten Morten (Gast)


Lesenswert?

Es wird beginnend vom String-Anfang gesucht, s wird auf die 
Anfangsadresse des Strings gesetzt:
1
s = str

Es wird so lange gesucht, so lange sich an der Position auf die s 
aktuell zeigt etwas ungleich 0 befindet:
1
*s

Dabei wird s nach jedem Test weiter gezählt, d.h. s zum nächsten Zeichen 
in str bewegt:
1
++s

Die Differenz aus der Position von s, an der das String-Ende gefunden 
wurde, und dem String-Anfang str ergibt die String-Länge:
1
s - str

von W.S. (Gast)


Lesenswert?

Das ist wie üblich auch wieder mal so unleserlich wie nur irgend möglich 
formuliert.

gestartet wird mit s = str;
weitergemacht wird, solange  *s; nicht Null ist
und in jeder Runde wird  s inkrementiert:  ++s

Anschließend wird die Differenz von s und str als Ergebnis genommen.

simpler wäre das mit
1
 int laenge;
2
 laenge = 0;
3
 while (*s)
4
 { ++s;
5
   ++laenge;
6
 }
7
 return laenge;
8
9
10
oder wenn s passend deklariert ist
11
und die CPU gut indexiert zugreifen kann
12
13
 int laenge;
14
 laenge = 0;
15
 while (s[laenge]) ++laenge;
16
 return laenge;

Bei heutigen Compilern bin ich mir fast sicher, daß dank Optimierung bei 
beiden Versionen der gleiche Maschinencode herauskommt.

W.S.

von zitter_ned_aso (Gast)


Lesenswert?

Frank schrieb:
> return(s - str);


Die Zeigerdifferenz ist ja vom Typ ptrdiff_t.

die Funktion liefert size_t zurück.

Kann es hier Probleme geben?

von Jörg B. (jbernau)


Lesenswert?

Arduino Fanboy D. schrieb:
> Es wird, die so genannte, "Pointer Arithmetik" verwendet.
> Google, oder seine Brüder, sollten dazu was finden.
> Auch gibt jedes C oder C++ Buch gerne dazu Auskunft.

Oder halt ein Leser aus diesem Forum:

Jeder C-String wird mit den ASCII Zeichen \0 oder dem Wert 0 
abgeschlossen.

Frank schrieb:
> for (s = str; *s; ++s)
>       {}
>      return(s - str);


Der Anfangswert von s wird auf die Speichadresse von str gesetzt. Danach 
wird  die Schleife so lange durchlaufen, wie der Inhalt der 
inkrementierten Speicheradresse verschieden von false / 0 ist. Am Ende 
wird die Differenz der Speicheradressen als Stringlänge zurückgegeben. 
Das haben die anderen zwar auch geschrieben, nur in einem Zusammenhang 
sehe ich es als didaktisch besser.
An diesem Beispiel kannst Du "Pointer Arithmetik" gut verstehen lernen.

VG

Jörg

: Bearbeitet durch User
von Einer K. (Gast)


Lesenswert?

zitter_ned_aso schrieb:
> Kann es hier Probleme geben?

Natürlich!
size_t ist vorzeichenlos.
Und die Differenz ptrdiff_t ist mit einem Vorzeichen behaftet.

Dein AVR(?) hat allerdings nicht soviel Speicher, dass das jemals 
greifen wird.

von H.Joachim S. (crazyhorse)


Lesenswert?

W.S. schrieb:
> Das ist wie üblich auch wieder mal so unleserlich wie nur irgend möglich
> formuliert.

Sehr schön :-)
Da kann der Programmierer zeigen, was er kann.

von Frank (Gast)


Lesenswert?

Hallo zusammen,

ich Danke euch für die Super Erklärungen !!!

Grüße

Frank

von Walter T. (nicolas)


Lesenswert?

W.S. schrieb:
> Das ist wie üblich auch wieder mal so unleserlich wie nur irgend möglich
> formuliert.

Hey, ihr habe den Teil vergessen:

Frank schrieb:
> const char *s;

von Dr. Sommer (Gast)


Lesenswert?

W.S. schrieb:
> simpler wäre das mit

Führe das mal auf einem 386-System mit einem String der Länge 3000000000 
aus. Oder auf einem AVR mit einem String der Länge 35000. Was fällt auf?

von Arno (Gast)


Lesenswert?

W.S. schrieb:
> Das ist wie üblich auch wieder mal so unleserlich wie nur irgend
> möglich
> formuliert.

Wir haben alle verstanden, dass du mit C auf Kriegsfuß stehst und daher 
in jedem C-Programm etwas unleserliches findest. Leserlichkeit liegt 
auch ein Stück weit im Auge des Betrachters, daher treffen da oft starke 
Meinungen aufeinander, die mit Fakten nur schlecht zu hinterlegen sind.

Allerdings verstehen Millionen Programmierer diverser 
Programmiersprachen for-Schleifen ebenso gut wie while-Schleifen. Deine 
Umschreibung macht es daher unleserlicher, weil die implizierte Aussage 
"Diese Schleife iteriert über..." verloren geht.

Und einen IMMO wesentlichen Leserlichkeitsfaktor hast du ausgelassen - 
statt
1
*s
würde ich auf
1
*s != '\0'
abfragen.

Und dein
1
while(BEDINGUNG) do;

ist unglaublich schlecht lesbar und sehr fehleranfällig bei Änderungen.

Nicht umsonst ist auch
1
while(BEDINGUNG)
2
{ do1;
3
  do2;
4
}
reichlich verpönt, weil dabei die geschweifte Klammer als Block-Beginn 
leicht verloren geht.

Dass das Mitzählen mittels separater Zählvariable neue Fallstricke 
einbaut, hat Dr. Sommer ja schon dargestellt. Demnach bin ich sehr 
sicher, dass deine Programme nicht den gleichen ASM-Code erzeugen wie 
eine funktionierende strlen-Implementierung.

MfG, Arno

von Purzel H. (hacky)


Lesenswert?

Das Beispiel zeigt wie mehrfach schlimm die Sprache C ist.
Kryptisch zu lesen, und maximal schrottige Konzepte.

Dann gibt es Leute, die kopieren einen String im Sinne von :

 for (i=0 ; i<strlen(); i++)
   mystring = mystring + anotherstring[i];

und dabei wird der String immer um einen Character verlaengert, nachdem 
das Ende gesucht worden ist. Einen String verlaengern kann eine 
neuallokation und kopieren bedeuten...
Wenn man so einen Text in ein Memo kopiert, kann fuer einen kurzen Text 
auch schon eine Viertelstunde vergehen.

von Axel S. (a-za-z0-9)


Lesenswert?

Adam P. schrieb:

sehr schlampig formuliert

> Frank schrieb:
>> s = str;
>
> Der Zeiger s wird auf das erste Zeichen des übergebenen Zeichenkette
> gelegt.

Der Zeiger str wird in den Zeiger s kopiert. So etwas wie "erstes 
Zeichen" gibt es möglicherweise gar nicht. Z.B. wenn str auf den 
Leerstring zeigt.

>> *s;
>
> Druchlaufe die for() solange wie der Zeiger s wahr ist, also ungleich 0
> bzw. NULL

Das ist komplett falsch. Es wird nicht geprüft, ob der Zeiger 0 ist, 
sondern ob das, auf das der Zeiger zeigt, 0 ist. Bzw. noch genauer wird 
das, worauf der Zeiger zeigt, in einen bool konvertiert und dann 
geprüft ob das true ist. Ausgeschrieben: (bool)(*s) == true

>> ++s
>
> Falls s noch ungleich 0, dann weiter mit nächstem Zeichen.

Die Bedingungsprüfung steckt nicht in diesem Ausdruck. Dieser Ausdruck 
stellt den Zeiger s um die Länge eines char weiter, sonst nichts.

>> return(s - str)
>
> Irgendwann ist s gleich 0 und es wird die Länge zurückgegeben, diese
> errechnet sich aus der Ende-Adresse minus der Start-Adresse (im RAM).

Jein. Pointer sind Pointer und keine Adressen. Pointerarithmetik ist 
diffizil und verschieden von reiner Adreßrechnung. Im Fall eines char* 
wird das Ergebnis meistens das gleiche sein. Aber bei einem int* 
ziemlich sicher nicht.

Korrekte Antwort: der Pointer s wurde für jedes Zeichen im String um 
eine Position weitergezählt. Die Differenz zwischen s am Ende der 
Schleife und dem Startwert in str ergibt also gerade die Anzahl der 
Zeichen.

von Walter T. (nicolas)


Lesenswert?

Irgendwie drückt ihr euch um die Erklärung, was an s jetzt konstant ist, 
wenn es doch inkrementiert wird.

von Zeno (Gast)


Lesenswert?

Arno schrieb:
> reichlich verpönt, weil dabei die geschweifte Klammer als Block-Beginn
> leicht verloren geht.

Und wie ist das mit den geschweiften Klammern im Eröffnungspost? Die 
gehen wohl nicht verloren?

von Adam P. (adamap)


Lesenswert?

Axel S. schrieb:
> sehr schlampig formuliert

Ja mag sein, dass es nicht wie ausm Lehrbuch ist - habe es versucht auf 
das wesentliche zu kürzen:

Denn wenn jmnd die for() nicht versteht, wird er mit den Fachbegriffen 
und den dahinter liegenden Themen wohl völlig erschlagen sein, oder?

Ansich hast du recht...wollts nicht komplizierter machen als es für den 
TO eh schon ist.

Aber entschuldige falls ich es dir recht machen konnte.

von Jim M. (turboj)


Lesenswert?

Walter T. schrieb:
> Irgendwie drückt ihr euch um die Erklärung, was an s jetzt konstant ist,
> wenn es doch inkrementiert wird.

Der Pointer s ist nicht konstant.

Der Speicherbereich auf den s und str zeigen wird nicht verändert - das 
wird hier mit dem const char ausgedrückt.

von Dr. Sommer (Gast)


Lesenswert?

Walter T. schrieb:
> Irgendwie drückt ihr euch um die Erklärung, was an s jetzt
> konstant ist,
> wenn es doch inkrementiert wird.

s ist natürlich nicht konstant, sondern der Speicher, auf den s zeigt. 
So wird dem Leser der Funktionssignatur signalisiert, dass strlen() das 
Ziel des Zeigers nicht modifiziert. Außerdem kann man innerhalb der 
Funktion nicht so leicht versehentlich drauf schreiben.

Wollte man "s" selbst konstant machen, müsste man es "char * const s" 
schreiben. Aber das ist hier natürlich nicht Sinn der Sache und 
funktioniert nicht.

von Marten Morten (Gast)


Lesenswert?

Walter T. schrieb:
> Irgendwie drückt ihr euch um die Erklärung, was an s jetzt konstant ist,
> wenn es doch inkrementiert wird.

Nicht s ist "const"ant, sondern das, worauf s zeigt. C Typdeklarationen 
werden von innen nach außen gelesen:

const char *s; --> s is a pointer to a constant character

von Einer K. (Gast)


Lesenswert?

Walter T. schrieb:
> Irgendwie drückt ihr euch um die Erklärung, was an s jetzt
> konstant ist,
> wenn es doch inkrementiert wird.

Das ist doch klar...
Der Pointer ist nicht konstant.
Er zeig nur auf einen konstanten Inhalt.

Ein konstanter Pointer auf einen konstanten Inhalt sieht so aus:
const char * const s = test; // C++

von Jim M. (turboj)


Lesenswert?

Übrigens sollte man sich bei solchen Fragen lieber gleich totes Holz 
(Buch genannt) wie den K&R besorgen.

C ist eine uralte Sprache und eher auf Maschinenlesbarkeit denn auf 
Lesbarkeit durch Menschen getrimmt.

Das heisst natürlich nicht das man nicht auch gut lesbaren C Code 
schreiben kann, aber hier im Beispiel wurde eine recht kompakte 
Schreibweise gewählt.
Die ist für Anfänger ohne Hilfe mit Literatur schlecht lesbar.

von A. S. (Gast)


Lesenswert?

Jim M. schrieb:
> C ist eine uralte Sprache und eher auf Maschinenlesbarkeit denn auf
> Lesbarkeit durch Menschen getrimmt.

Das sehe ich anders. In Mathematik und Physik bevorzugt man auch kurze 
prägnante Schreibweisen und kommentiert das ggf.. Beispiele:

 * E=mc²
 * v=a*t

Ein Code mit "sprechenden Namen" wie oben ist für strlen einfach nur 
grauselig. In einem Lehrbuch oder Beispielcode schreibt man dann halt 
ran, was s ist und was passiert.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Arduino Fanboy D. schrieb:
> Er zeig nur auf einen konstanten Inhalt.

Falsch. Man kann durchaus einen Poiner auf einen nicht-konstanten-Inhalt 
runtergeben, z.B.
1
char buf[128];
2
int len;
3
...
4
len = strlen (buf);  // buf ist NICHT vom Typ "const char *"

Das const im Funktionsparameter heisst lediglich, dass die Funktion 
selbst den Inhalt nicht verändern wird.

Aber tröste Dich: W.S. hat das auch nie verstanden.

von Einer K. (Gast)


Lesenswert?

Frank M. schrieb:
> Aber tröste Dich: W.S. hat das auch nie verstanden.
Spassvogel!

Ob der String außerhalb der Funktion konstant ist, ist vollig egal.
Für das Innere der Funktion ist er konstant.

Der Kompiler schreit einen an, wenn man *s = 42; probiert.

Der Zeiger zeigt auf einen (für die Funktion) konstanten Inhalt.

Veränderung unterbunden === konstant

Das Schlüsselwort const dient dazu "Dinge" als konstant zu deklarieren.

Die Frage war:
"Wieso kann man einen const Zeiger ändern?"

Antwort:
Nicht der Zeiger wird als konstant deklariert, sondern das worauf er 
zeigt!


> Falsch.
Und jetzt sage nochmal: Falsch!

-------
Gerne darfst du mich falsch verstehen wollen!
Auch gerne in der Öffentlichkeit demütigen wollen.

> hat das auch nie verstanden.
Danke!

von my2ct (Gast)


Lesenswert?

W.S. schrieb:
> simpler wäre das mit int laenge;
>  laenge = 0;
>  while (*s)
>  { ++s;
>    ++laenge;
> ...

Simpler für die Ausführung ist es, wenn man nicht zwei verschiedene 
Größen inkrementieren muss.

Manchmal ist Verarbeitungsgeschwindigkeit wichtiger als die geistige 
Unbeweglichkeit des geneigten Lesers.

von A. S. (Gast)


Lesenswert?

my2ct schrieb:
> Manchmal ist Verarbeitungsgeschwindigkeit wichtiger

Aber nur ganz selten, in sicher weniger als 10 von 1000 Zeilen. Hier 
z.b. sind beide Varianten sicher gleich schnell.

Die Version mit 2 Inc ist nur einfach schlechter lesbar und schlechter 
wartbar (für einen Programmierer, nicht für einen Schüler)

von Arno (Gast)


Lesenswert?

Zeno schrieb:
> Arno schrieb:
>> reichlich verpönt, weil dabei die geschweifte Klammer als Block-Beginn
>> leicht verloren geht.
>
> Und wie ist das mit den geschweiften Klammern im Eröffnungspost? Die
> gehen wohl nicht verloren?

Die stehen für sich in einer eigenen Zeile, da passiert es deutlich 
weniger leicht, dass man sie überliest.

MfG, Arno

von meckerziege (Gast)


Lesenswert?

Viel Verwirrung hier kommt leider davon dass einige C nicht ausreichend 
beherrschen. Der Code oben ist grundsätzlich in Ordnung. Je nach Coding 
Guidelines KÖNNTE eine solche strlen Funktion aber unschön sein (z.B. 
würde ich empfehlen die Schleifenbedingung explizit zu formulieren als 
(*s != 0), da *s vom Typ her nicht direkt true/false ist.)

Die oben gezeigte strlen Funktion ist SEHR einfach. Und auch nicht 
sonderlich performant, besonders nicht für längere Strings auf allen 
Architekturen. Daher sollte man so etwas nicht explizit selbst schreiben 
sondern sich auf die optimierten Libs verlassen für den jeweiligen 
Prozessor.


(ich gehe im folgenden einfach mal von 32-bit ARM aus)
Das strlen oben operiert auf 8 Bit. Es wird pro 8 Bit ein Lesezugriff 
per Pointer durchgeführt. Die Maschine ist aber 32 bit breit. D.h. pro 
Transaction auf dem Bus würden 4 mal soviel Daten drüber gehen 
(=ineffizient!). Dazu kommt noch dass pro Zeichen eine 
Schleifeniteration gemacht wird was (je nach Branch Prediction 
Fähigkeiten) auch teuer sein kann. Zumindest wird viel gesprungen was 
Performance kostet.

SOWAS ist eine komplexe Strlen Funktion, die dann auch gar nicht mehr in 
C geschrieben wurde:
http://yann.poupet.free.fr/arm/optimized_strlen.html

Das operiert gleich mal auf 32 bit Worten und geht das geschickter an.

von Walter S. (avatar)


Lesenswert?

Name H. schrieb:
> Das Beispiel zeigt wie mehrfach schlimm die Sprache C ist.
> Kryptisch zu lesen, und maximal schrottige Konzepte.
>
> Dann gibt es Leute, die kopieren einen String im Sinne von :
>
>  for (i=0 ; i<strlen(); i++)
>    mystring = mystring + anotherstring[i];

in welcher Sprache ist dein Beispiel geschrieben?

von Johnny B. (johnnyb)


Lesenswert?

Walter S. schrieb:
> Name H. schrieb:
>> Das Beispiel zeigt wie mehrfach schlimm die Sprache C ist.
>> Kryptisch zu lesen, und maximal schrottige Konzepte.
>>
>> Dann gibt es Leute, die kopieren einen String im Sinne von :
>>
>>  for (i=0 ; i<strlen(); i++)
>>    mystring = mystring + anotherstring[i];
>
> in welcher Sprache ist dein Beispiel geschrieben?

In C# würde das so funktionieren.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.