Peter II schrieb:> ordentlicher ist strlen, schneller ist sizeof
Kommt darauf an, ob strlen() eine externe Funktion ist oder "builtin"
ist.
Der gcc generiert jedenfalls für beide Varianten denselben Code.
Peter II schrieb:> ordentlicher ist strlen, schneller ist sizeof
In diesem speziellen Fall sind sie typischerweise gleichwertig.
Wenn man es allgemeiner betrachtet, muss man beachten, dass sizeof nur
mit konstanten Arrays das gewünschte Ergebnis bringt, während strlen
ganz allgemein mit Strings funktioniert, dafür aber (sofern der Inhalt
dem Compiler nicht schon bekannt ist) zur Laufzeit durch den String
iterieren muss und daher mehr Zeit braucht.
Peter II schrieb:> ordentlicher ist strlen, schneller ist sizeof
Habe ich vermutet, da der Compiler vermutlich schon das sizeof()-1
ausrechnet und als festen Wert in den Code einträgt.
Das wird vermutlich auch bei
1
#define MY_STRING "Das ist ein Test"
2
3
chartestString[]=MY_STRING;
4
...
5
{
6
uint8len;
7
/* Option 1 */
8
len=strlen(MY_STRING);
9
/* Option 2 */
10
len=sizeof(MY_STRING)-1;
11
}
gelten, so lange man den Inhalt von testString nicht ändert.
Wenn ich das hier mache:
1
#define MY_STRING "Das ist ein Test"
2
3
chartestString[]=MY_STRING;
4
...
5
{
6
uint8len;
7
8
testString[4]=0;
9
/* Option 1 */
10
len=strlen(MY_STRING);
11
/* Option 2 */
12
len=sizeof(MY_STRING)-1;
13
}
Bekomme ich bei Option 1 logsicherweise dann 3 und Option2 weiterhin
(konstant) 16.
Oder?
Jochen schrieb:> Peter II schrieb:>> ordentlicher ist strlen, schneller ist sizeof>> Habe ich vermutet, da der Compiler vermutlich schon das sizeof()-1> ausrechnet und als festen Wert in den Code einträgt.
Ja, natürlich wird sizeof() immer zur Compilezeit ermittelt. Aber in
Deinem konkreten Fall, wo es sich um eine Zeichenkonstante handelt, die
zur Compilezeit bekannt ist, ersetzt der Compiler den Aufruf von
strlen() direkt durch die Konstante.
Beweis:
1
#define MY_STRING "Das ist ein Test"
2
3
int
4
main()
5
{
6
uint8_tlen;
7
8
len=strlen(MY_STRING);
9
// len = sizeof(MY_STRING)-1;
10
11
PORTD=len;// damit das Ergebnis nicht wegoptimiert wird
12
}
Output:
1
00000080 <main>:
2
80: 80 e1 ldi r24, 0x10 ; 16
3
82: 8b b9 out 0x0b, r24 ; 11
4
84: 80 e0 ldi r24, 0x00 ; 0
5
86: 90 e0 ldi r25, 0x00 ; 0
6
88: 08 95 ret
Hier wird PORTD einfach mit 16 geladen. Das ist derselbe Code wie bei
der sizeof-Variante.
Markus F. schrieb:> ???
sizeof() liefert die gesamte Länge, strlen() liefert die Länge bis zur
ersten 0x00. Woher soll das Programm zur Laufzeit auch wissen, dass der
Programmierer da höheren Schwachsinn geschrieben hat.
Markus F. schrieb:> und jetzt die Preisfrage: was passiert bei> #define MY_STRING "Das ist ein Test\0und nochwas"
strlen: 16
sizeof: 28
Beide Male setzt der Compiler einfach die Konstante in den
Assembler-Code ein. strlen() wird also weiterhin nicht aufgerufen.
Ich bin immer wieder begeistert, was der Compiler alles weg optimieren
kann. Dass er strlen() durch eine Konstante ersetzen kann hätte ich gar
nicth erwartet. Ein super Programm ist das, ich mag den gcc.
Georg G. schrieb:> Markus F. schrieb:>> ???>> sizeof() liefert die gesamte Länge, strlen() liefert die Länge bis zur> ersten 0x00. Woher soll das Programm zur Laufzeit auch wissen, dass der> Programmierer da höheren Schwachsinn geschrieben hat.
Das ist meiner Ansicht nach kein Schwachsinn (weder hoch noch tief) -
ich verwende das beispielsweise für Texte, wenn ich nur zwei Sprachen
brauche.
Wollte damit zeigen, daß sizeof() - 1 möglicherweise keine gute Idee als
Ersatz für strlen() ist.
Moin,
Wenn man mal noch lustigere Zeichen als simples ASCII nimmt, also
irgendwelche UTF-irgendwas Dinger, wirds wohl auch Abweichungen zwischen
strlen und sizeof geben.
Gruss
WK
strlen ist besser, da es ausdrückt was gemeint ist. sizeof()-1 dauert 3x
länger zu verstehen (ausser natürlich in so kleinen Programmen). Wenn
das Programm mal geändert wird auf char* , dann liefert sizeof einen
falschen Wert.
Stefan U. schrieb:> Ich bin immer wieder begeistert, was der Compiler alles weg optimieren> kann. Dass er strlen() durch eine Konstante ersetzen kann hätte ich gar> nicth erwartet. Ein super Programm ist das, ich mag den gcc.
Der kann sogar die Funktionen aus math.h ersetzen, wenn man die mit
Konstanten aufruft.
Markus F. schrieb:> Wollte damit zeigen, daß sizeof() - 1 möglicherweise keine gute Idee als> Ersatz für strlen() ist.
Genau das wollte ich auch sagen. Sizeof() wird nur zur Compile Zeit
ausgewertet. Ich hätte erwartet, dass der Compiler bei einem String, der
nicht als const definiert ist, das strlen() nicht als Funktion
eliminiert. Im Gegensatz zu stefanus halte ich das für einen Fehler.
Georg G. schrieb:> Im Gegensatz zu stefanus halte ich das für einen Fehler.
Wenn der Compiler keinen Weg sieht, wie der String etwas anderes
als konstant sein kann, darf er das, ansonsten natürlich nicht.
Zur Klarstellung:
Bei den Beispielen wurde der String immer mit #define str "tralala"
definiert. Damit kann er zur Laufzeit nie verändert werden.
Definiert man ihn mit char str[] = "tralala", wird strlen() immer
aufgerufen.
Georg G. schrieb:> Bei den Beispielen wurde der String immer mit #define str "tralala"> definiert. Damit kann er zur Laufzeit nie verändert werden.>> Definiert man ihn mit char str[] = "tralala", wird strlen() immer> aufgerufen.
das kann nicht sein.
Der Compiler sieht darin keinen Unterschied. der Preprozessor ersetzt ja
schon die defines.
Georg G. schrieb:> Dann probier es aus.>> char str0[] = "Hello World";
Hängt damit zusammen, dass str0 hier global sichtbar ist und daher
von anderen Modulen geändert werden könnte.
Mach es mal „static“.
Geh mal auf 4.9 oder größer.
Nicht immer wurde mit neueren Versionen das AVR-Backend besser, aber der
Compiler selbst hat massiv dazugelernt.
Ich verwende 6.2 und bin mir sicher, daß der keine Probleme hat, die
Länge dieses faktisch konstanten Strings (statisch, nicht verändert) zur
Compile-Time zu ermitteln.
Wobei, wenn man weiß, daß man ihn nicht ändern möchte, ein "const" um
das zu dokumentieren nicht falsch ist.
Wenn ich das Päd. Wieder weggelegt hab, kann ich es ja mal testen.
Jörg W. schrieb:> Georg G. schrieb:>> Dann probier es aus.>>>> char str0[] = "Hello World";>> Hängt damit zusammen, dass str0 hier global sichtbar ist und daher> von anderen Modulen geändert werden könnte.>> Mach es mal „static“.
das hat damit nichts zu tun.
1
charstr0[]="Hello World";/* 1 */
2
char*str1="Hello World";/* 2 */
3
#define str2 "Hello World" /* 3 */
gcc legt str0 (veränderbar) im .data Segment ab. Str1 und str2 (wenn
benutzt) werden als Literale im .text (oder .rodata) Segment abgelegt
und sind damit per Definition unveränderbar. Ein const braucht's dazu
nicht.
Georg G. schrieb:> Dann probier es aus.>> char str0[] = "Hello World";> #define str1 "tralala"> i = strlen(str1);
ok, ich bin von folgenden ausgegangen:
>> gcc legt str0 (veränderbar) im .data Segment ab. Str1 und str2 (wenn> benutzt) werden als Literale im .text (oder .rodata) Segment abgelegt> und sind damit per Definition unveränderbar. Ein const braucht's dazu> nicht.
Nicht so ein AVR-GCC, denn der muß mit 2 separaten Adressräumen umgehen
und legt Texte nur dann in FLASH ab, wenn man ihn dazu zwingt.
Carl D. schrieb:> Nicht so ein AVR-GCC, denn der muß mit 2 separaten Adressräumen umgehen> und legt Texte nur dann in FLASH ab, wenn man ihn dazu zwingt.
String-Literale sind in C nicht beschreibbar. Auch wenn das auf dem AVR
möglich ist, ist das Verhalten undefiniert wenn man das auch macht.
Damit kann der Compiler davon ausgehen, dass ein als Literal gegebener
String sich zur Laufzeit nicht verändert und entsprechend optimieren.
Ein extra const braucht es dafür nicht.
Andreas B. schrieb:> String-Literale sind in C nicht beschreibbar. Auch wenn das auf dem AVR> möglich ist, ist das Verhalten undefiniert wenn man das auch macht.
Früher gab's mal -fwritable-strings (wurde allerdings -
vernünftigerweise - abgeschafft).
Andreas B. schrieb:> Carl D. schrieb:>> Nicht so ein AVR-GCC, denn der muß mit 2 separaten Adressräumen umgehen>> und legt Texte nur dann in FLASH ab, wenn man ihn dazu zwingt.>> String-Literale sind in C nicht beschreibbar. Auch wenn das auf dem AVR> möglich ist, ist das Verhalten undefiniert wenn man das auch macht.>> Damit kann der Compiler davon ausgehen, dass ein als Literal gegebener> String sich zur Laufzeit nicht verändert und entsprechend optimieren.> Ein extra const braucht es dafür nicht.
Ich habe nichts von der Beschreibbarkeit von String-Konstanten
geschrieben, sondern von der Tatsache, daß ein AVR mehrere Adressräume
hat. Der Compiler kann eine String-Konstante nicht einfach in den
FLASH-Bereich stecken, den ein solcher String hat keine Adresse im
Adress-Raum, in dem ein char str[10] liegt.
Strings sind im RAM-Adress-Raum, const char Flash[] = "Flash" steht im
FLASH-Adress-Raum.
Beide beginnen bei 0, auch wenn im RAM-Adress-Raum bis 0x60/0x100
erstmal Register/Io kommt.
Es gibt spezielle Befehle, um auf Konstanten im FLASH zuzugreifen
Was braucht's noch, um den Unterschied zu einem ARM mit 4GB linearem
Adress-Raum zu verstehen?
Jörg W. schrieb:> Georg G. schrieb:>> Dann probier es aus.>>>> char str0[] = "Hello World";>> Hängt damit zusammen, dass str0 hier global sichtbar ist und daher> von anderen Modulen geändert werden könnte.>> Mach es mal „static“.
Das ändert nichts, da str0 trotzdem hintenherum über Zeigerzugriffe
verändert werden könnte. Im vorliegenden Beispiel könnte der Compiler
diesen Fall zwar ausschließen, da das Programm nur aus einer einzigen
Funktion (main) besteht, in der keinerlei Zeigergewurschtel stattfindet.
In etwas kopmplexeren Programmen geht aber diese Sorte von Analyse um
zu viele Ecken und ist deswegen für den Compiler zu schwer.
Mit "const" sieht die Sache anders aus, da hiermit der Programmierer dem
Compiler zusichert, dass str0 nirgends im Programm geändert wird, auch
nicht über Zeigertricks.
Ebenfalls zur Compilezeit ausgewertet wird strlen in diesem Beispiel,
und das sogar ohne die Verwendung von "const":
1
intmain(void){
2
chari;
3
charstr2[]="Hello World";
4
5
/*
6
Hier kann beliebiger Code mit Ausnahme von
7
- direkten Schreibzugriffen auf str2,
8
- Schreibzugriffen über Zeiger und
9
- Aufrufen externer Funktionen
10
stehen.
11
*/
12
13
i=strlen(str2);
14
PORTA=i;
15
16
return0;
17
}
Hier kann der Compiler leicht sehen, dass zwischen der Definition von
str2 und dem strlen-Aufruf der String nicht verändert wird, so dass die
Optimierung angewendet kann.
Sobald aber anstelle des Kommentars eine Anweisung wie diese hier
1
PORTA=42;
steht, ist es vorbei mit der Optimierung, da die Zuweisung an PORTA ein
(etwas kaschierter) Zugriff über Zeiger ist, der (zumindest in den Augen
des Compilers) str2 überschreiben könnte. Deswegen wird hier strlen zur
Laufzeit und nicht zur Compilezeit ausgeführt.
Edit:
Macht man in diesem Beispiel str2 static, wird ebenfalls nicht mehr
optimiert. Das sich da Ganze in der main-Funktion abspielt, besteht
eigentlich keine Gefahr, dass str2 irgendwo überschreiben wird. Jedoch
behandelt GCC main wie jede andere C-Funktion. Und würde die Funktion im
Beispiel nicht main, sondern func heißen, könnte es tatsächlich sein,
dass vor dem Aufruf von func ein Stück Code ausgeführt wird, der über
abenteuerliche Zeigeroperationen str2 verändert.
Georg G. schrieb:>> sizeof() liefert die gesamte Länge, strlen() liefert die Länge bis zur> ersten 0x00. Woher soll das Programm zur Laufzeit auch wissen, dass der> Programmierer da höheren Schwachsinn geschrieben hat.
Es ist durchaus in C üblich auf diese Weise stringlisten zu definieren.
Beispiel:
Hans-Georg L. schrieb:> Es ist durchaus in C üblich auf diese Weise stringlisten zu definieren.
wer macht so etwas wenn es Array gibt?
[c]
int main()
{
char *list[] = { "text_1", "text_2", "text_3", "text_4", 0 };
int i = 0;
while( list[i] != 0 )
{
printf("%s\r\n", list[i] );
}
}
Peter II schrieb:> Hans-Georg L. schrieb:>> Es ist durchaus in C üblich auf diese Weise stringlisten zu definieren.>> wer macht so etwas wenn es Array gibt?>> [c]> int main()> {> char *list[] = { "text_1", "text_2", "text_3", "text_4", 0 };>> int i = 0;> while( list[i] != 0 )> {> printf("%s\r\n", list[i] );> }> }
Das ist was Anderes: ein Zeigerarray auf Stringliterale. Belegt für
jeden der Texte nochmal einen Pointer. Bei deinem Beispiel und 32
Bit-Zeigern also mal eben so rund 70% Overhead.
Kann man so machen (ich mach's - meist - auch so), aber wenn's eng wird,
nicht unbedingt besser ...
Daniel A. schrieb:> Peter II schrieb:>> Hans-Georg L. schrieb:>>> Es ist durchaus in C üblich auf diese Weise stringlisten zu definieren.>>>> wer macht so etwas wenn es Array gibt?>> Microsoft, natürlich! Beispiel:> https://msdn.microsoft.com/en-us/library/ms683187(v=vs.85).aspx
Environment-Variablen haben ja nun nichts in erster Linie mit
Microsoft zu tun.
Dass man diese erstmal nur linear im Speicher anordnen kann, ist
völlig logisch, denn sie kommen ja eben daher, was ihr Name sagt:
aus der Umgebung (des aufgerufenen Prozesses). Dort liegen sie
einfach nur hintereinander in einem Stück Speicher.
Unter Unix baut der Startup-Code dann dafür noch ein Pointer-Array
(im Stack) auf, welches als dritter Parameter an main() übergeben
wird. Dass die eigentlichen Strings im Speicher hintereinander liegen,
kann man sich aber im Debugger ansehen:
Markus F. schrieb:> Kann man so machen (ich mach's - meist - auch so), aber wenn's eng wird,> nicht unbedingt besser ...
Ist halt der übliche space/time tradeoff: ohne diese Zeiger musst
du jedesmal durch alles durchtippeln.
Yalu X. schrieb:> Edit:>> Macht man in diesem Beispiel str2 static, wird ebenfalls nicht mehr> optimiert. Das sich da Ganze in der main-Funktion abspielt, besteht> eigentlich keine Gefahr, dass str2 irgendwo überschreiben wird. Jedoch> behandelt GCC main wie jede andere C-Funktion. Und würde die Funktion im> Beispiel nicht main, sondern func heißen, könnte es tatsächlich sein,> dass vor dem Aufruf von func ein Stück Code ausgeführt wird, der über> abenteuerliche Zeigeroperationen str2 verändert.
Diese abenteuerlichen Zeigeroperationen rufen aber in C undefiniertes
Verhalten hervor, somit darf ja Mist rauskommen, wenn str2 verändert
wurde. Der Compiler muss das also eigentlich nicht berücksichtigen.
Jörg W. schrieb:>>> wer macht so etwas wenn es Array gibt?>>>> Microsoft, natürlich! Beispiel:>> https://msdn.microsoft.com/en-us/library/ms683187(v=vs.85).aspx>> Environment-Variablen haben ja nun nichts in erster Linie mit> Microsoft zu tun.
Nein, aber dass es um Environment-Variablen geht, ist ja hier nicht das
Problem, sondern die Art, wie die API-Funktion sie zurückgibt. Und die
ist auf Microsofts Mist gewachsen.
Rolf M. schrieb:> Diese abenteuerlichen Zeigeroperationen rufen aber in C undefiniertes> Verhalten hervor
Nicht unbedingt. Wenn die Funktion einen Zeiger auf die als static
deklarierte lokale Variable (bspw. über den Rückgabewert) anderen
Programmteilen zu Verfügung stellt, dürfen diese damit die Variable ganz
legal verändern. Beim nächsten Aufruf der Funktion steht dann in der
Variable etwas anderes, obwohl sie lokal ist.
Ok, in diesem Fall ist es die Funktion selbst, die die Adresse ihrer
lokalen Variable anderen Programmteilen preisgibt. Der Compiler könnte
die Optimierung also auf diejenigen Fälle beschränken, wo er sicher sein
kann, dass der Array-Zeiger nicht nach außen gelangt. Dazu müsste er nur
die Funktion selbst (und keine externen Programmteile) analysieren, was
ja prinzipiell machbar sein sollte, da die dafür benötigten Algorithmen
zur Datenflussanalyse sowieso schon existieren.
Yalu X. schrieb:> Rolf M. schrieb:>> Diese abenteuerlichen Zeigeroperationen rufen aber in C undefiniertes>> Verhalten hervor>> Nicht unbedingt. Wenn die Funktion einen Zeiger auf die als static> deklarierte lokale Variable (bspw. über den Rückgabewert) anderen> Programmteilen zu Verfügung stellt, dürfen diese damit die Variable ganz> legal verändern.
Klar, aber das ist nicht das, was ich unter "abenteuerliche
Zeigeroperationen" verstehe.