strlen-Funktion Funktionsweise

Gast #5743171
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
#5743190
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.
Gast #5743194
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
Gast #5743204
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.
#5743217
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
Gast #5743593
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
#5743638
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.
#5743649
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.
#5743666
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.
Gast #5743670
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.
#5743679
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.
Gast #5743830
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.
Moderator Persönliche Seite #5743873
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.
Gast #5743884
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!
Gast #5744002
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
Gast #5744030
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.
#5744614
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren