Hallo, ich habe ein verständnisproblem zu dem Befehl sizeof() in c. z.B. const uint8_t x[] = {0x00, 0x01, 0x02}; sizeof(x) ergibt 24. Um die Anzahl der elemente im array zu ermittlen, mache ich sizeof(x)/(sizeof(uint8_t)); Jetzt habe ich zwei arrays const char y[] = {"abc"}; const char z[3] = {"abc"}; sizeof(y) ergibt 4 und sizeof(z) ergibt 3. Warum bekomme ich eine größe von 4, wenn ich die Länge des Arrays nicht direkt mit angebe?
weil ein Stringliteral (der Text zwischen den ") mit einem '\0' beendet wird. C-Grundlagen
Das sizeof(x) sollte 3 ergeben. Die Anzahl der Elemente bekommst du besser mit sizeof(x)/sizeof(x[0]) Dann spielt der Typ keine Rolle mehr. Mach das nicht bei Funktionsparametern. Die Länge eines Strings bekommst du mit der Standardfunktion strlen(). Das funktioniert aber nicht bei deinem z, weil die Funktion die '\0' sucht.
Dirk B. schrieb: > Das funktioniert aber nicht bei deinem z, weil die Funktion die '\0' > sucht. ??? strlen(y) und strlen(z) ergeben beide 3 bei mir. Ist das jetzt nur Zufall?
@Jens ...und wenn Du Dich auf den Kopf stellst, sieht alles verkehrt herum aus. Oder anders ausgedrückt: Was haben strlen und sizeof miteinander zu tun?
Sebastian S. schrieb: > Oder anders ausgedrückt: Was haben strlen und sizeof miteinander zu tun? nichts, aber mein Problem hat man ja in meinem ersten Beitrag hoffentlich verstanden. Dann wurde mir gesagt, was ich falsch gemacht habe. Das habe ihc jetzt verbessert. Aber es wurde auch gesagt, dass strlen(z) nicht geht. Wenn ich das jedoch ausführe, kommt auch 3 heraus. Das war jetzt eine neue frage warum. Oder sollte ich dafür extra einen neuen Beitrag eröffnen? Ich denke nicht.
Es ist ein array of char ohne nullterminierung. Strlen funktioniert nur zufällig weil hinter dem array z eine weitere Variable ist welche auf null gesetzt ist. Im realen Code ist dem dann nicht so und es kommt dann auf dem Zufall an was zurückgeliefert wird.
Chris schrieb: > Es ist ein array of char ohne nullterminierung. > Strlen funktioniert nur zufällig weil hinter dem array z eine weitere > Variable ist welche auf null gesetzt ist. > Im realen Code ist dem dann nicht so und es kommt dann auf dem Zufall an > was zurückgeliefert wird. Ahso, daran wird es gelegen haben. Wenn ich nur
1 | const unsigned char y[] = {"abc"}; |
2 | const unsigned char z[3] = {"abc"}; |
3 | printf("%d %d", strlen(y), strlen(z)) |
mache, bekomme ich als ausgabe 3 und 18 heraus. Vielen dank für den Hinweis
Jens schrieb: > const uint8_t x[] = {0x00, 0x01, 0x02}; > > sizeof(x) ergibt 24 Eventuell habe ich das falsch in Erinnerung, aber liefert sizeof() nicht die Größe in Bytes? Das Array hat 3 Elemente die jeweils ein Byte groß sind, daher würde ich behaupten sizeof(x)= 3. Oder ist uint8_t 8 Bytes groß weil das ein 64 bit System ist und der Compiler macht sich da keine Mühe irgendwas kleineres zu verwenden/optimiert?
Soduko schrieb: > Oder ist uint8_t 8 Bytes groß weil das ein 64 bit System ist und der > Compiler macht sich da keine Mühe irgendwas kleineres zu > verwenden/optimiert? Eine Größe ungleich drei ist niemals zulässig.
Jemand schrieb: > Soduko schrieb: >> Oder ist uint8_t 8 Bytes groß weil das ein 64 bit System ist und der >> Compiler macht sich da keine Mühe irgendwas kleineres zu >> verwenden/optimiert? > > Eine Größe ungleich drei ist niemals zulässig. https://www.youtube.com/watch?v=FFAbhi6xnCo leo
Soduko schrieb: > Oder ist uint8_t 8 Bytes groß weil das ein 64 bit System ist und der > Compiler macht sich da keine Mühe irgendwas kleineres zu > verwenden/optimiert? Ein Compiler, der keinen Typ für exakt 8 Bits hat, darf keinen Typ uint8_t definieren, sondern nur uint_least8_t.
:
Bearbeitet durch User
Jemand schrieb: > Eine Größe ungleich drei ist niemals zulässig. Naja, der Code ist ja nur bruchstückhaft. Wer weiss, wer wie warum uint8_t definiert hat. Aber ja, wenn sizeof(char) ungleich sizeof(uint8_t) ist, dann läuft da einiges schief.
A. S. schrieb: > Aber ja, wenn sizeof(char) ungleich sizeof(uint8_t) ist, dann läuft da > einiges schief In der Tat: sizeof(char) ist per Definition immer 1, denn sizeof liefert die Größe in Vielfachen von char, und char ist nunmal genau 1 char groß. Außerdem ist char immer minimal 8bit groß. Einen kleineren Datentyp kann es nicht geben. Wenn char also genau 8 bit ist, ist uint8_t ein Alias dafür (bzw. für unsigned char). Ist char mehr als 8 bit, kann es kein uint8_t geben. Somit ist sizeof (uint8_t) immer entweder ein Compiler Fehler oder 1.
Jens schrieb: > daran wird es gelegen haben. Wenn ich nur > > const unsigned char y[] = {"abc"}; > const unsigned char z[3] = {"abc"}; > printf("%d %d", strlen(y), strlen(z)) > > mache, bekomme ich als ausgabe 3 und 18 heraus. > Vielen dank für den Hinweis Das Ergebnis von strlen(z) ist dem Zufall geschuldet. strlen geht Zeichen für Zeichen den Speicher durch und hört erst bei der ersten Null (nicht das Zeichen '0') auf zu zählen. In Deinem Fall steht im Speicher hinter dem Array z irgendwelches anderes Zeug, das halt irgendwo zufälligerweise Null ist.
Jens schrieb: > const unsigned char y[] = {"abc"}; > const unsigned char z[3] = {"abc"}; > printf("%d %d", strlen(y), strlen(z)) > > mache, bekomme ich als ausgabe 3 und 18 heraus. %d (bei printf) ist für int da. Der Rückgabetyp von strlen() ist aber size_t. Der richtige Formatspecifier ist %zu - z für size_t - u für unsigned Die Größe von size_t ist so gewählt, das die Größe jedes Elements dort abgelegt werden kann. Auf einem 32-Bit System reicht dafür eine 32-Bit Variable aus. Das passt meist mit dem unsigned int. Auf einem 64-Bit System aber nicht mehr. Und ein int ist auch dort meist 32-Bit groß. "meist", weil es auch anders sein kann. Darauf musst du als Programmierer aber achten, sonst läuft dein Programm irgendwann (z.B. nach System-/Compiler-/Versions-update) nicht mehr richtig) Der Compiler kann das erkennen und eine Warnung ausgeben. Wenn du ihn läßt bzw. entsprechend einstellst.
:
Bearbeitet durch User
Rufus Τ. F. schrieb: > In Deinem Fall steht im Speicher hinter dem Array z irgendwelches > anderes Zeug, das halt irgendwo zufälligerweise Null ist. Es kann auch sein, das es in der Debug-Version immer richtig ist und in der Release-Version dann daneben geht.
"Richtig" ist das auch in der Debugversion nicht. Es ist möglich, daß es dann irgendwelches Padding gibt, und der Zufall sich dann anders verhält.
Jens schrieb: > const char y[] = {"abc"}; > const char z[3] = {"abc"}; Der Grund, warum sizeof() hier unterschiedliche Größen liefert, ist relativ einfach. Bei der Initialisierung eines char-Arrays mit einem C-String wird immer versucht, noch hinter dem String die '\0' als Terminierung anzubringen. Aber nur, wenn es auch passt! Sonst wird auf die Terminierung (stillschweigend) verzichtet. Da bei y[] keine Größe angegeben ist, wird das Array ausreichend groß mit Länge 4 angelegt und mit "abc" und anschließender Terminierung initialisiert. Bei z[3] ist die Länge 3 vorgegeben und es wird daher darauf verzichtet, das Terminierungszeichen mit in das Array reinzuschreiben. Das ist kein Fehler in C, sondern vom C-Standard so gewollt. Leider ist es aber äußerst unschön: Wir erhalten hier einen unterminierten String, der so in 99% aller Fälle bestimmt nicht erwünscht ist. Meines Erachtens wäre eine Fehlermeldung hier angebrachter. Geändert kann das Verhalten aber nicht mehr, denn es ist schon seit Jahrzehnten so Standard.
:
Bearbeitet durch Moderator
Es hat schon seinen Sinn. Ein Beispiel eines Codes #define match_(x) else if \ ( id[0]==x[0]&&id[2]==x[2] \ &&strlen(x)==len \ &&id[len-2]==x[len-2] \ &&(__builtin_constant_p (x) ? !strcmp_P(id,x) : !strcmp(id,x)) \ ) #define match(x) match_(PSTR(#x)) #define Match(x) if(0); match(x) Hier werden Ausschnitte aus dem String als byte oder eventuall auch als word/dword (optimiert, nicht hier) verglichen. Wenn es jetzt einen Error geben würde wegen des fehlenden Null oder weil der ganze String nicht verwendet wird, sondern nur Ausschnitte, dann wären viele Optimierungen in C nicht möglich. Im Prinzip wird hier das String mit 4 Konstanten gecheckt bevor ein strcmp angewendet wird. Bei einer Variablen ist es auch optimiert. Strings mit nur einem Zeichen werden vorher abgefangen, deshalb fehlt dieser Check für len.
chris schrieb: > Ein Beispiel eines Codes Darin sehe ich ein strlen. Das auf einen nicht-nullterminierten String loszulassen ist ein eindeutiger Fehler.
Rufus Τ. F. schrieb: > Darin sehe ich ein strlen. Das auf einen nicht-nullterminierten String > loszulassen ist ein eindeutiger Fehler. Wenn ich das Macro richtig verstehe, soll x ein nullterminierter String und id[] dabei ein eventuell nicht-terminierstes Byte-Array sein. Aber so ganz erschließt sich mir nicht, wo da eine Optimierung sein soll. Erst Prüfung, ob erstes und drittes Zeichen gleich, dann strlen(), dann am Schluss doch strcmp(). strlen() kostet auch jede Menge Zeit. Aber egal, was hat der Beitrag von chris mit meinem Argument zu tun, dass eine Initialisierung mit einem C-String "abc" besser immer mit Terminierung durchgeführt werden sollte? Man würde auf jeden Fall viele Fehler, die auch bestimmt deswegen in der Vergangenheit aufgetreten sind, direkt durch eine ensprechende Compiler-Meldung erkennen.
:
Bearbeitet durch Moderator
const char z[3] = {"abc"}; Ein strlen() auf einen nicht nullterminierten String ist Ansatzpunkt für Hacker. Geschickt ausgenutzt kann die Programmkontrolle übernommen werden oder Zugriff auf eigentlich geschützte Daten ermöglicht werden. Meine Meinung: Programmierer die das wiederholt machen, sollten eine Kündigung bekommen!
Ein Match(setup), wobei setup zu "setup" verwandelt wird, und angenommen es gibt so etwas wie diesen vereinfachten Code ein Match(enable) wird in ein if(0); else if(id[0]=='e'&&id[2]=='a'&&6==len&&id[len-2]=='l'&&!strcmp_P(id,PSTR("en able"))) umgewandelt. Das ist dann ziemlich optimiert, auch wenn es 200 Vergleiche sind. wird hingegen match_(foo) verwendet wo foo ein char* ist, dann wird if(0); else if(id[0]==foo[0]&&id[2]==foo[2]&&strlen(foo)==len&&id[len-2]==foo[len-2] &&!strcmp(id,foo)) Dies ist auch noch optimiert, ein strlen wird nur gemacht, wenn der erste und dritte Buchstabe gleich sind. Ein Stringvergleich wird erst gemacht wenn noch der Vorletzte Buchstabe identisch ist. Minimum sind zwei Buchstaben, in diesem Falle ist der Dritte Buchstabe '\0'. Bei einem konstanten String wird strlen sowie die Stringzugriffe wegoptimiert, eben wegen der Arrayzuweistung/definition von String[pos]. Viele Hash oder find Routinen von LinkListen oder HashList/Table machen solche vergleiche, und da merkt man dann richtig den Geschwindigkeitsunterschied, aber auch bei einem Microcontroller mit 50 Strings, der nebenbei andere Sachen macht. parse_line(char*line) { unsigned char i,len; unsigned int j; char*id; char* arg_s[4]; uint32_t arg_n[4]; assert(line); if(!line) return; while(isspace(*line)) line++; if(id=strchr(line,';')) *id='\0'; if(*line=='!') { comment(line); return; } if(!*line) return; arg_f=0; for(i=0;i<tblsize(arg_s);i++) { // parse line arguments arg_n[i]=atol(arg_s[i]=args(line,&line)); if(isdigit(*arg_s[i])) arg_f|=1<<i; } if(line) { args2x: serror("too many arguments"); return; } for(i=0;i<tblsize(arg_s);i++) if(!*arg_s) { arg_n[0]=i-1; break; } j=strlen(id=arg_s[0]); if(j>16) goto err; len=j; if(!isalpha(*id)||(len=strlen(id))<2) serror(NULL); else if(state==S_SETUP) { Match(setup) state=S_SETUP; match(help) goto help; else goto err; Match(help); else goto command; } else if(state==S_ENABLE) { Match(setup) state=S_SETUP; match(help) goto help; else goto err; } else if(state==S_HIDDEN) { /* code */ } else if(state==S_DEFAULT) { Match(enable) { /* check password */ state=S_ENABLE; } match(help) goto help; else goto err; } else err: serror("unrecognized command"); return; help: /* help headers */ if(state==S_DEFAULT) { /* code */ } if(state==S_SETUP) { /* code */ } if(state==S_HIDDEN) { /* code */ } if(state==S_ENABLE) { /* code */ } return; command: // store command token to eeprom i=arg_n[0]; arg_n[0]=hash(id); #if tblsize(arg_s)!=4 #error "config must be updated for tblsize(arg_s)" #endif config(i|arg_f<<3); j=0; for(i=0;i<len;i++) if(arg_n[i])<=0xff) j|=1<<i; for(i=0;i<len;i++) if(arg_n[i])<=0xffff) j|=0x100<<i; if(j>>8) { j|=0x80; config(j); config(j>>8); } else config(j); while(i--) { if(j&(0x100<<i) config(arg_n[i]>>16); if(j&(0x1 <<i) config(arg_n[i]>> 8); config(arg_n[i]); } }
chris schrieb: > if(id=strchr(line,';')) *id='\0'; > if(*line=='!') { comment(line); return; } > if(!*line) return; arg_f=0; > for(i=0;i<tblsize(arg_s);i++) { // parse line arguments > arg_n[i]=atol(arg_s[i]=args(line,&line)); > if(isdigit(*arg_s[i])) arg_f|=1<<i; > } if(line) { args2x: serror("too many arguments"); return; } > for(i=0;i<tblsize(arg_s);i++) if(!*arg_s) { arg_n[0]=i-1; break; } > j=strlen(id=arg_s[0]); if(j>16) goto err; len=j; > if(!isalpha(*id)||(len=strlen(id))<2) serror(NULL); else Eine Pull Request mit sowas würde ich kommentarlos löschen.
Hier geht es aber darum dass "foobar"[3] ein oft verwendetes Konstrukt in C ist, natürlich versteckt von Preprozessoraufrufen wo eine Teilmenge von einem konstanten C oder Unicode String in ein Array oder Strukt umgewandelt wird und dabei teile des Strings verlorengehen. Ich habe dazu ein Beispiel geliefert.
chris schrieb: Viel Zeugs, wie ... > for(i=0;i<tblsize(arg_s);i++) if(!*arg_s) { arg_n[0]=i-1; break; } Was hast du genommen? Wo gibt's das? leo
#define tblsize(tbl) (sizeof(tbl)/sizeof(tbl[0])) args(line,&line) ist ähnlich strtod liefert String zurück, ähnlich strtok Ja, ein Kopierfehler. for(i=0;i<tblsize(arg_s);i++) if(!*arg_s) { arg_n[0]=i-1; break;} richtig ist: for(i=0;i<tblsize(arg_s);i++) if(!*arg_s[i]) { arg_n[0]=i-1; break;} es gibt ein #define arg(x,y...) CONCAT(arg_,x)(y) mit unterschiedlichen Macros für a, n, f, y usw. Z.B wertet arg(y,2) aus ob das zweite Argument 1,up, yes, Y, T enthält und liefert dann 1 oder eben 0 zurück. Auch wenn solche Makros die Leserlichkeit erhöhen, aus Übersichtsgründen habe ich diese ersetzt, und da ist mir dieser Flüchtigkeitsfehler untergelaufen.
chris schrieb: > Auch wenn solche Makros die Leserlichkeit erhöhen, Du hast rein gar nichts verstanden. Zu +90% der Zeit wird ein Code gelesen - nicht geschrieben, deiner ist unleserlich. Makros, die nur dir bekannt sind, helfen nicht. Dieser write-once code sollte besser bei dir in deinem Keller verbleiben. leo
@chris Kannst ein vollstaendiges kompilierbares minimal beispiel von deinem parser posten? Wuerde das gerne mal durch einen aktuellen gcc oder clang jagen
chris schrieb: > Auch wenn solche Makros die Leserlichkeit erhöhen Erhöhen hätte nur [ c] und [/c] gekonnt (kennst Du die hier?). Bei mehr als 2 Zeilen geht es nicht ohne.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.