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
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.
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.
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
intlaenge;
2
laenge=0;
3
while(*s)
4
{++s;
5
++laenge;
6
}
7
returnlaenge;
8
9
10
oderwennspassenddeklariertist
11
unddieCPUgutindexiertzugreifenkann
12
13
intlaenge;
14
laenge=0;
15
while(s[laenge])++laenge;
16
returnlaenge;
Bei heutigen Compilern bin ich mir fast sicher, daß dank Optimierung bei
beiden Versionen der gleiche Maschinencode herauskommt.
W.S.
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
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.
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.
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;
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?
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
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.
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.
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?
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.
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.
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.
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
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++
Ü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.
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.
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
charbuf[128];
2
intlen;
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.
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!
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.
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)
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
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.
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?
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.