Forum: Compiler & IDEs Größe eines #define String mit sizeof ermitteln


von Andi_T T. (andi_t)


Lesenswert?

Hallo zusammen,

ich möchte die Größe eines Strings auslesen, wird später zur
Weiterverarbeitung benötigt.

#define VERS_PROGRAMVERSION “V01.00.00“  /* Programmversion */
uint8_t temp;

temp = sizeof (VERS_PROGRAMVERSION);

Ist dies mit sizeof zulässig und verlässlich?

Im Simulator und auch auf der HW (ATMEGA64) scheint es zu funktionieren,
es stellt sich allerdings die Frage, ob es hier je nach Compiler-Version
zu bösen Überraschungen kommen kann.

Kann dies jemand beantworten, vorab vielen Dank für eine Rückmeldung.

Grüße
Andreas

von Philipp Klaus K. (pkk)


Lesenswert?

sizeof liefert zuverlässig die Länge eines string literal, 
einschließlich der 0 am Ende.

strlen liefert die Länge eines string, ohne die 0 am Ende.

Ein weiterer Unterscheid ist, dass strlen nur die Länge bis zur ersten 0 
liefert, sizeof beim string literal dagegen die bis zur letzten.

Beispiele:
1
strlen("test"); // 4
2
sizeof("test"); // 5
3
strlen("test\0test"); // 4
4
sizeof("test\0test"); // 10
5
char *test = "test";
6
strlen(test); // 4
7
sizeof(test); // sizeof(char +)

von Gerald K. (geku)


Lesenswert?

Ja, funktioniert temp enthält 10, die Größe der Zeichenkette "V01.00.00" 
+ /0
1
#define VERS_PROGRAMVERSION "V01.00.00" /* Programmversion */
2
3
int main(int argc, char* argv[])
4
{
5
        uint8_t temp;
6
7
        temp = sizeof (VERS_PROGRAMVERSION);
8
        printf("<%d>\r\n",temp);
9
10
}

von Andi_T T. (andi_t)


Lesenswert?

Ok super, herzlichen Dank, jetzt verstehe ich auch die +1 bei sizeof
(habe nicht an die /0) gedacht. Nochmals vielen Dank an beide.

Grüße Andreas

von Gerald K. (geku)


Lesenswert?

Andi_T T. schrieb:
> jetzt verstehe ich auch die +1 bei sizeof
> (habe nicht an die /0) gedacht.

sizeof liefert die Objektgröße im Speicher zurück. Das Objekt String 
besteht aus den Zeichen + dem Endzeichen /0.
strlen liefert die Anzahl der Zeichen bis zum Endzeichen /0  zurück.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

ich würde so schreiben.
1
const char PROGRAMVERSION [] = "V01.00.00";

: Bearbeitet durch User
von Noch ein Kommentar (Gast)


Lesenswert?

> ich würde so schreiben.

Stimmt, inzwischen hat sich jeder an const und inline gewöhnt - wenn ich 
ein #define sehe, kommt sofort das Vermutung auf, das ist uralter Code, 
Altlasten.

von wozu? (Gast)


Lesenswert?

Andi_T T. schrieb:
> “V01.00.00“

Bei diesem Format  wird sich die Stringlänge überhaupt nicht ändern.

"V01.01.00", "V01.05.01", "V01.20.01"

von Nikolaus S. (Firma: Golden Delicious Computers) (hns)


Lesenswert?

Beachte:
1
#define VERS_PROGRAMVERSION “V01.00.00“  /* Programmversion */
2
sizeof(VERS_PROGRAMVERSION)
berechnet der C-Compiler indem er die Zeichen im String im Sourcecode 
zählt.
1
#define VERS_PROGRAMVERSION “V01.00.00“  /* Programmversion */
2
strlen(VERS_PROGRAMVERSION)
berechnet der Prozessor und es braucht Speicherplatz der Länge 
sizeof(VERS_PROGRAMVERSION), die in einer Schleife durchgezählt werden. 
Der Compiler legt dazu einen unbenannten String im Speicher ab.
1
const char PROGRAMVERSION [] = "V01.00.00";
2
sizeof(PROGRAMVERSION)
berechnet zwar auch der Compiler (weil er die Arraylänge kennt) aber das 
braucht trotzdem Speicher... Ausser er kann es wegoptimieren (hängt von 
verschiedenen Faktoren ab ob das geht).
Speicher und Rechenzeit spart man also mit der ersten Variante am 
meisten.

von Markus F. (mfro)


Lesenswert?

... oder einfacher: sizeof ist ein (Compile-time) Operator
strlen() ist eine Funktion.
1
a = sizeof(b++)

macht nicht das, was (vielleicht) mancher denkt.

von A. S. (Gast)


Lesenswert?

Noch ein Kommentar schrieb:
> Stimmt, inzwischen hat sich jeder an const und inline gewöhnt - wenn ich
> ein #define sehe, kommt sofort das Vermutung auf, das ist uralter Code,
> Altlasten.

Oder C-Code. Da ist const was völlig anderes.

von Rolf M. (rmagnus)


Lesenswert?

Nikolaus S. schrieb:
> #define VERS_PROGRAMVERSION “V01.00.00“  /* Programmversion */
> strlen(VERS_PROGRAMVERSION)
> berechnet der Prozessor und es braucht Speicherplatz der Länge
> sizeof(VERS_PROGRAMVERSION), die in einer Schleife durchgezählt werden.
> Der Compiler legt dazu einen unbenannten String im Speicher ab.

Dann hast du aber einen schlechten Compiler erwischt. Etwas bessere 
machen das natürlich auch komplett zur Compilezeit. Beispielsweise der 
avr-gcc (da von ATmega die Rede ist):
1
#include <avr/io.h>
2
#include <string.h>
3
4
#define VERS_PROGRAMVERSION "V01.00.00"  /* Programmversion */
5
6
int main()
7
{
8
    PORTB = strlen(VERS_PROGRAMVERSION);
9
    return 0;
10
}
erzeugt folgenden Assembler-Code:
1
.file   "strlen.c"
2
__SP_H__ = 0x3e
3
__SP_L__ = 0x3d
4
__SREG__ = 0x3f
5
__tmp_reg__ = 0
6
__zero_reg__ = 1
7
        .section        .text.startup,"ax",@progbits
8
.global main
9
        .type   main, @function
10
main:
11
/* prologue: function */
12
/* frame size = 0 */
13
/* stack size = 0 */
14
.L__stack_usage = 0
15
        ldi r24,lo8(9)
16
        out 0x18,r24
17
        ldi r24,0
18
        ldi r25,0
19
        ret
20
        .size   main, .-main
21
        .ident  "GCC: (GNU) 5.4.0"
Wie man sieht, wird die 9 einfach direkt an den Port geschrieben, und 
der String kommt überhaupt nicht vor.

von A. S. (Gast)


Lesenswert?

Rolf M. schrieb:
> Wie man sieht, wird die 9 einfach direkt an den Port geschrieben, und
> der String kommt überhaupt nicht vor.

Das geht aber nur mit der Vereinbarung, dass strlen keine externe 
Funktion.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

A. S. schrieb:
> Das geht aber nur mit der Vereinbarung, dass strlen keine externe
> Funktion.

Also mit -fhosted, was beim GCC implizit gesetzt ist.

von Rolf M. (rmagnus)


Lesenswert?

A. S. schrieb:
> Rolf M. schrieb:
>> Wie man sieht, wird die 9 einfach direkt an den Port geschrieben, und
>> der String kommt überhaupt nicht vor.
>
> Das geht aber nur mit der Vereinbarung, dass strlen keine externe
> Funktion.

Bei einer hosted-Implementation darf der Compiler die Annahme treffen, 
dass sich die Funktion strlen() aus <string.h> genau wie im Standard 
definiert verhält und auf dieser Basis dann optimieren. Das macht gcc 
nicht nur bei strlen(), sondern auch bei sehr vielen weiteren 
Standard-Funktionen.

von A. S. (Gast)


Lesenswert?

Rolf M. schrieb:
> Bei einer hosted-Implementation darf der Compiler die Annahme treffen,
> dass sich die Funktion strlen() aus <string.h> genau wie im Standard
> definiert verhält und auf dieser Basis dann optimieren.

Genau das hättest Du sofort dazu sagen sollen, statt:

Rolf M. schrieb:
> Dann hast du aber einen schlechten Compiler erwischt.

Es ist halt ein Unterschied, ob ein Compiler eine Funktion aufruft oder 
dessen Ergebnis ermittelt.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

A. S. schrieb:
> Es ist halt ein Unterschied, ob ein Compiler eine Funktion aufruft oder
> dessen Ergebnis ermittelt.

Ja, sicher. Aber im C-Standard gilt die "as if"-Regel: der Code muss 
sich so verhalten, als ob die abstrakte Maschine ihn ausgeführt hätte. 
Da die Funktionen der Standardbibliothek genormt sind, darf der Compiler 
sein Wissen über diese Funktionen dafür einsetzen, ein vorab bekanntes 
Ergebnis eben nicht erst durch den Aufruf der Funktion zu ermitteln, 
sondern sofort einzusetzen.

Gute Compiler machen das, wenn man es ihnen nicht ausdrücklich verbietet 
(-ffreestanding).

: Bearbeitet durch Moderator
von Rolf M. (rmagnus)


Lesenswert?

A. S. schrieb:
> Es ist halt ein Unterschied, ob ein Compiler eine Funktion aufruft oder
> dessen Ergebnis ermittelt.

Ja, der Unterschied liegt in diesem Fall darin, dass letzterer besser 
optimiert.

von A. S. (Gast)


Lesenswert?

Jörg W. schrieb:
> Ja, sicher. Aber im C-Standard gilt die "as if"-Regel: der Code muss
> sich so verhalten, als ob die abstrakte Maschine ihn ausgeführt hätte.

Das ist alles klar. Er hätte es nur einfach anmerken sollen. Sonst denkt 
der TO, dass es bei anderen Funktionen (die nicht stdlib sind) auch 
geht, wenn der Compiler sie kennt. "Weil das bei C++ ja auch geht".

Es ist ein Sonderfall, dann soll man es auch sagen. Oder noch besser, 
die Bedingungen erwähnen. Nicht einfach "Dein Compiler ist blöd".

von Rolf M. (rmagnus)


Lesenswert?

A. S. schrieb:
> Jörg W. schrieb:
>> Ja, sicher. Aber im C-Standard gilt die "as if"-Regel: der Code muss
>> sich so verhalten, als ob die abstrakte Maschine ihn ausgeführt hätte.
>
> Das ist alles klar. Er hätte es nur einfach anmerken sollen. Sonst denkt
> der TO, dass es bei anderen Funktionen (die nicht stdlib sind) auch
> geht, wenn der Compiler sie kennt.

Das geht eingeschränkt auch bei solchen Funktionen, allerdings muss der 
Compiler dann auch an der Stelle des Aufrufs den Quellcode der Funktion 
kennen, denn irgendwo muss er ja die Beschreibung dessen, was sie tut 
her bekommen.
Bei den Standardfunktionen muss er den Quellcode prinzipiell nicht 
kennen, da er die Beschreibung aus dem Standard hat, den er ja selbst 
implementiert. Er darf annehmen, dass sie sich so verhalten, wie der 
Standard das vorschreibt.

> "Weil das bei C++ ja auch geht".
>
> Es ist ein Sonderfall, dann soll man es auch sagen. Oder noch besser,
> die Bedingungen erwähnen. Nicht einfach "Dein Compiler ist blöd".

Es ging hier konkret um strlen() im Vergleich zu sizeof und um die 
Behauptung , dass strlen() mehr Aufwand an Rechenzeit und Speicher 
produziere, weil es zwingend zur Laufzeit ausgeführt werden müsse. Das 
stimmt aber nicht, und optimierende Compiler nutzen dieses Potenzial 
auch seit vielen Jahren. Und da es hier um AVR geht, habe ich auch 
gleich noch für den avr-gcc gezeigt, dass dieser das tut.
Nirgends habe ich behauptet, dass der Compiler sämtliche Funktionen auf 
der Welt zur Compilezeit ausführen könne.

: Bearbeitet durch User
von Andi_T T. (andi_t)


Lesenswert?

Vielen Dank für die rege Diskussion und Beiträge, d.h. der Umsetzung 
(#define du sizeof) spricht nichts entgegen.
Gruß Andreas

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.