Hi, habe das Problem die wahre Größe einer übergeordneten struct herauszufinden, in der structs mit Strings enthalten sind. Ich benötige die Größe, da der Inhalt über die serielle Schnittstelle gesendet werden muß. Target ist STM32 Core0/Core3 mit GCC 4.7.4 //in def_struct.h Definitionen der mehrfach verwendeten structs struct Ebene3{ uint8_t size; uint8_t type; char name[]; /* hier ist die Länge des Strings noch nicht bekannt! */ }; struct Ebene4{ uint8_t size; uint8_t type; uint8_t offslng; uint8_t option_class; char name[]; /* hier ist die Länge des Strings noch nicht bekannt! */ }; struct Head{ uint8_t size; uint8_t type; }; //--------- // in funktion.h wird die übergeordnete struct definiert: struct SENSOR_PARA_DESCR{ struct Ebene3 SensorZelle; struct Ebene4 UminZelle; struct Ebene3 SensorStrom; /* ... weitere structs */ struct Head EndOfDescr1; }; // in funktion.c wird der struct nun angelegt und initialisiert #pragma pack(push) #pragma pack(1) const struct SENSOR_PARA_DESCR Sensor_Para_Descr = { .SensorZelle = { sizeof(Sensor_Para_Descr.SensorZelle)+sizeof("Zelle minV") ,PARDSC_MEASURE ,"Zelle minV" } ,.UminZelle = { sizeof(Sensor_Para_Descr.UminZelle)+sizeof("") ,PARDSC_MSBDATA ,ofs(0) // Parameter offset ,OPTBIT_NON | MPXWK_VOLT // Messwert 0.1Volt ,"" } ,.SensorStrom = { sizeof(Sensor_Para_Descr.SensorStrom)+sizeof("Strom I") ,PARDSC_MEASURE ,"Strom I" } /* ... weitere structs */ ,.EndOfDescr1 = { sizeof(Sensor_Para_Descr.EndOfDescr1) ,PARDSC_END_OFD } }; const uint8_t Sensor_EndOf_Para_Descr = 0; #pragma pack(pop) // in Sendefunktion liefert sizeof(Sensor_Para_Descr) eine zu kleine size, die strings sind nicht berücksichtigt. Nächster Versuch über die Adressen des Inhalts zur Laufzeit weiter zu kommen: txdata_count = (void*)&Sensor_Para_Descr.EndOfDescr1 - (void*)&Sensor_Para_Descr; klappt auch nicht, die Adresse des letzten internen structs wird auch falsch (ohne die Strings zu Berücksichtigen) übergeben. Ohne eingeschaltete Optimierung funktioniert der Trick mit einer anschliessenden const Variablen hinter dem struct: txdata_count = (void*)&Sensor_EndOf_Para_Descr - (void*)&Sensor_Para_Descr; mit Optimierung wird die variable Sensor_EndOf_Para_Descr aber vor den struct gelegt, geht also auch nicht. Was nun? Mache ich hier einen Fehler, gibt es Tipps von den GCC Gurus hier? Im Voraus vielen Dank Ingo
das kann so nicht gehen: char name[]; ist ein Zeiger. Die Daten liegen also überhaupt nicht innerhalb der Struct. Damit ist auch klar das ein Sizeof - was ja zur compilezeit ausgewertet wird nicht wissen kann wie groß die Daten sind.
Peter II schrieb: > das kann so nicht gehen: > > char name[]; > > ist ein Zeiger. Die Daten liegen also überhaupt nicht innerhalb der > Struct. Doch, tun sie. Und es ist kein Zeiger.
Johann L. schrieb: > Doch, tun sie. Und es ist kein Zeiger. Und "legal" sind sie auch: http://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html
Wobei es sich syntaktisch um "flexible array members" von C99 handelt und GCC eine etwas erweiterte Syntax / Semantik erlaubt.
Gibt es nicht eine Compiler Anweisung, die auch bei Optimierung dem Compiler anweist, die anschliessende const Variable hinter dem struct, dort zu belassen und nicht beliebig wo anders hin zu legen? Damit wäre ja schon mein Problem gelöst. Gruß Ingo
Ingo Stahl schrieb: > Gibt es nicht eine Compiler Anweisung, die auch bei Optimierung dem > Compiler anweist, die anschliessende const Variable hinter dem struct, > dort zu belassen und nicht beliebig wo anders hin zu legen? Er legt sie nicht wo anders hin. Die Variable hat aber eine Größe von 0, kann also keine Daten aufnehmen. Ingo Stahl schrieb: > // in Sendefunktion > liefert sizeof(Sensor_Para_Descr) eine zu kleine size, die strings sind > nicht berücksichtigt. Natürlich nicht. Die Größe eines Datentyps ist fix. Die kann nicht mit jeder Instanz anders sein. Die Größe steht fest, wenn du den Typ definierst, nicht erst wenn du eine Instanz anlegst.
Die variable hat die Größe uint8_t, also ein Byte mit dem Wert 0. Das sie je nach Optimierungsstufe nicht immer hinter den structs liegt, kann man im Debugger beobachten. Hier ist es ja so, das die string-size ja schon zur compile time bekannt ist, der GCC dies aber nicht verarbeitet und zur Laufzeit nicht die richtigen Adressen der internen structs liefert. Und das finde ich besonders krass! Um das Problem schnell aus der Welt zu schafffen habe ich einen Vierzeiler geschrieben, der zur Laufzeit einmal durch die structs geht, bis er den struct mit der ENDE-Kennung gefunden hat und die Länge des äußeren structs bereitstellt. Gruß Ingo
Ingo Stahl schrieb: > Und das finde ich besonders krass! Ich finde es krass, dass du die Warnungen ausgeschaltet hast oder ignorierst. Bei mir kommen zumindest überall, wo du diese Strukturen erzeugst, die Warnungen: Warnung: ungültige Verwendung einer Struktur mit flexiblem Feldelement und: Warnung: Initialisierung eines flexiblen Feld-Elements Lies mal: http://gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc/Zero-Length.html
Ingo Stahl schrieb: > Hier ist es ja so, das die string-size ja schon zur compile time bekannt > ist, der GCC dies aber nicht verarbeitet und zur Laufzeit nicht die > richtigen Adressen der internen structs liefert. > > Und das finde ich besonders krass! Der Compiler kennt die String-Größe nicht immer. Stell dir vor, du hast in einer anderen Quelldatei eine Extern-Verweis auf Sensor_Para_Descr. Dort kann der Compiler zur Größenbestimmung nur die Struct-Deklaration von SENSOR_PARA_DESCR, nicht aber die Variablendefinition heranziehen. Würde der Compiler zwei unterschiedliche Größen liefern, je nachdem, ob nur die Struct-Deklaration oder zusätzlich auch die Variablendefinition bekannt ist, wäre das noch viel krasser. Das eigentlich Problem liegt darin, dass die Initialisierung eines flexible Array-Members durch den C-Standard nicht abgedeckt ist. Die Verwendung einer Struktur mit einem flexible Array-Member als nichtletztes Element einer übergeordneten Struktur ist nach meinem Verständnis zwar erlaubt, ergibt aber wenig Sinn. Wie schon von Rolf angemerkt, erntest du entsprechende Warnungen, wenn du dem Compiler die Option -pedantic mitgibst. Der eigentliche Sinn der flexible Array-Members besteht darin, dass man bei dynamisch (mit malloc) erzeugten Strukturen ein einzelnes, ganz am Ende der Struktur stehendes Array variabel gestalten kann. Dazu muss beim malloc-Aufruf die Größe der Struktur plus die gewünchte Array-Größe als Argument übergeben werden. GCC erlaubt auch die Verwendung und die Initialisierung von flexible Array-Members in normalen Variablen, aber auch hier funktioniert das nur dann korrekt, wenn das Array ganz am Ende der Struktur steht.
Auf der anderen Seite kennt der Compiler die wahre Größe zum Schluss dann doch, wenn er weiteren Code oder const Daten hinter die äußere structur packt. Die gleichen Structuren habe ich schon beim WinAVR eingesetzt, nur ohne die flexiblen structuren in eine äußere einzupacken. Dort hatte ich auch schon festgestellt, das sizeof(mystruct) nicht funktioniert, aber die Adressen der nahtlos definierten und initialisierten structuren stimmten zumindest. Beim STM32 hatte ich dann das Problem, das trotz #pragma pack(1) zwischen den Structuren dann doch ein Alignment greifte, was ich nicht gebrauchen kann und das ich dann durch Integration in eine übergeordnete Struktur lösen konnte. Gruß Ingo
>Das eigentlich Problem liegt darin, dass die Initialisierung eines >flexible Array-Members durch den C-Standard nicht abgedeckt ist. Verzeihung, wenn ich hier eine Seitenfrage stelle. Ist das einfach nicht erwähnt oder steht das ausdrücklich im Standard? Leider habe ich nur die Drafts. Steht darüber was im K&R? (Konnten leider nichts dazu finden). Gibt es ein anderes Buch, wo man das nachlesen könnte? Wäre nett. Ich würde das sehr gerne mal nachlesen.
Das erste was mich in diesem Zusammenhang wunderte das bei: struct Ebene4{ uint8_t size; uint8_t type; uint8_t offslng; uint8_t option_class; char name[]; }; mit der Initialisierung zur compile time z.B.: const struct Ebene4 UminZelle = { sizeof(UminZelle) ,1 ,2 ,3 ,"meinText" }; sizeof(UminZelle) zum Eintrag der wahren Größe nicht funktioniert, wobei doch alle Informationen zu diesem Zeitpunkt bereits vorhanden sind. Und man händisch sizeof(meinText) noch dazu addieren muss damit es stimmt: const struct Ebene4 UminZelle = { sizeof(UminZelle)+sizeof(""meinText") ,1 ,2 ,3 ,"meinText" }; Der Compiler vom VC6++ machte das aber korrekt, wenn ich mich richtig erinnere. Gruß Ingo
Was berechnert er denn? sizeof(UminZelle) sollte 4 sein.
Genau, er liefert 4, die Länge vom String unterschlägt er obwohl sie eigentlich bekannt sein sollte. Oder geht der GCC nur einmal über die Sourcen und kann keine forward Referenzen wie ein Makro Assembler? Gruß Ingo
Ingo Stahl schrieb: > Genau, er liefert 4, die Länge vom String unterschlägt er obwohl sie Er unterschlägt garnix. Für sizeof wird der Typ des Datums bestimmt und dann dessen Größe anhand des ABI bestimmt (Alignment, Gaps, etc.). "struct Ebene4" ist unb bleibt ein incomplete Type der Größe 4, egal welche Objekte davon später erzeugt werden. Ich erlaube mir, aus dem C99-Standard zu zitieren (§6.7.2.1 #16) >> As a special case, the last element of a structure with more >> than one named member may have an incomplete array type; >> this is called a flexible array member. In most situations, >> the flexible array member is ignored. In particular, the size >> of the structure is as if the flexible array member were omitted >> except that it may have more trailing padding than the omission >> would imply. > eigentlich bekannt sein sollte. Oder geht der GCC nur einmal über die > Sourcen und kann keine forward Referenzen wie ein Makro Assembler? C ist kein Makro Assembler und GCC ist es auch nicht. Übrigens machen C / C++ auch keine Aussage über die Ablage / Reihenfolge von Objekten im Speicher. Auch wenn eine bestimmte Reihenfolge beobachtet wurde, bedeutet dies nicht, dass sie immer so bleiben wird. Aber das hast du ja schon selbst festgestellt...
:
Bearbeitet durch User
Williwilliwallawalla schrieb: >>Das eigentlich Problem liegt darin, dass die Initialisierung eines >>flexible Array-Members durch den C-Standard nicht abgedeckt ist. > > Verzeihung, wenn ich hier eine Seitenfrage stelle. > Ist das einfach nicht erwähnt oder steht das ausdrücklich im Standard? > Leider habe ich nur die Drafts. Die Drafts reichen. Hier ist der entsprechende Auszug aus dem C11-Draft, aus dem hervorgeht, was alles erlaubt ist und was nicht, und wie die Größe des Structs definiert ist:
1 | As a special case, the last element of a structure with more than one |
2 | named member may have an incomplete array type; this is called a |
3 | flexible array member. In most situations, the flexible array member is |
4 | ignored. In particular, the size of the structure is as if the flexible |
5 | array member were omitted except that it may have more trailing padding |
6 | than the omission would imply. However, when a . (or ->) operator has a |
7 | left operand that is (a pointer to) a structure with a flexible array |
8 | member and the right operand names that member, it behaves as if that |
9 | member were replaced with the longest array (with the same element type) |
10 | that would not make the structure larger than the object being accessed; |
11 | the offset of the array shall remain that of the flexible array member, |
12 | even if this would differ from that of the replacement array. If this |
13 | array would have no elements, it behaves as if it had one element but |
14 | the behavior is undefined if any attempt is made to access that element |
15 | or to generate a pointer one past it. |
Danach kommen ein paar Beispiele, u.a. dieses, wo die Initialisierung des flexible Array-Member explizit als invalid gekennzeichnet ist:
1 | struct s t1 = { 0 }; // valid |
2 | struct s t2 = { 1, { 4.2 }}; // invalid |
3 | t1.n = 4; // valid |
4 | t1.d[0] = 4.2; // might be undefined behavior |
Der GCC hält sich genau an diese Vorgaben, allerdings sollte er IMHO bei der invaliden Initialisierung einen Error statt einer Warning ausgeben. Wenn VC6 die Größe anders bestimmt, ist das ein Bug.
@ yalu vielen Dank für die ausführliche Erklärung. Bei zur Laufzeit angelegten Structuren kann ich die Einschränkungen ja durchaus verstehen. Zur Compile time, wenn alle notwendigen Informationen für ein const Objekt vorliegen, hatte ich mit solchen Einschränkungen aber nicht gerechnet. Habe ich die Aufgabenstellung eventuell falsch umgesetzt, es gibt sicher noch andere Wege nach Rom? Ich benötige einen Datensatz (zum senden an ein externes Gerät), der im Flash liegt, aus 20 bis 30 flexiblen structuren besteht, die nahtlos hintereinander liegen. eine dieser flexiblen structuren hat immer den Aufbau: struct flex{ uint8_t size; uint8_t type; uint16_t maxwert; /* ... weitere variablen */ char name[]; }; Im Kopf steht immer die Größe und dahinter der Typ, damit ist der Empfänger des Datensatzes in der Lage von Struktur zur Struktur zu gehen und diese in Verbindung mit den Struktur Definitionen entsprechend auszuwerten. Gruß Ingo
Ich würde es in etwa so machen.
1 | #define ADC0Type 0x3B // Name für Programm
|
2 | char ADC0name[] = "ADC0"; // Name für User |
3 | |
4 | struct flex |
5 | {
|
6 | uint8_t size; // max 256 byte |
7 | uint8_t type; // max 256 Typen |
8 | uint16_t maxwert; // maximaler Wert von payload |
9 | uint16_t payload; // |
10 | char *name; // null terminierter String |
11 | };
|
12 | |
13 | uint8_t Low( uint16_t u16wert ) |
14 | {
|
15 | return (( u16wert >> 0 ) & 0xFF); |
16 | }
|
17 | |
18 | uint8_t High( uint16_t u16wert ) |
19 | {
|
20 | return (( u16wert >> 8 ) & 0xFF); |
21 | }
|
22 | |
23 | void SendByte( uint8_t byte ) |
24 | {
|
25 | UsartSendByte(byte); |
26 | }
|
27 | |
28 | void SendWord( uint8_t word ) |
29 | {
|
30 | SendByte( Low( word) ); |
31 | SendByte( High(word) ); |
32 | }
|
33 | |
34 | void SendFlex( struct flex *fl ) |
35 | {
|
36 | char *name = fl->name; |
37 | |
38 | SendByte( fl->size ); |
39 | SendByte( fl->type ); |
40 | SendWord( fl->maxwert ); |
41 | SendWord( fl->payload ); |
42 | for( ; *name!=0; name++ ) |
43 | {
|
44 | SendByte( *name ); |
45 | }
|
46 | SendByte( 0 ); |
47 | }
|
48 | |
49 | int main(void) |
50 | {
|
51 | struct flex ADC0 = { |
52 | .type = ADC0Type, |
53 | .maxwert = 256, |
54 | .name = ADC0name, |
55 | };
|
56 | |
57 | // Größe der struct berechnen
|
58 | // size = 1 Byte
|
59 | // type = 1 Byte
|
60 | // maxwert = 2 Byte(s)
|
61 | // payload = 2 Byte(s)
|
62 | // Summe von flex = 6 Bytes
|
63 | |
64 | // strlen = 4
|
65 | // +1 für die Null -> 5
|
66 | // Summe gesammt = 6+5 = 11 Bytes
|
67 | // Entweder mit sizeof
|
68 | ADC0.size = sizeof(struct flex) + strlen(ADC0.name) + 1; |
69 | // oder statisch machen
|
70 | ADC0.size = 6 + strlen(ADC0.name) + 1; |
71 | |
72 | while( 1==1 ) |
73 | {
|
74 | // Messung eines analogen Wertes von ADC0
|
75 | ADC0.payload = getADC0(); |
76 | // Wert Senden
|
77 | SendFlex( &ADC0 ); |
78 | }
|
79 | |
80 | return 0; |
81 | }
|
Vielen Dank für die Mühe. Da ich aber 20 bis 30 dieser Strukturen habe, ist diese Art "zu Fuss" zu übertragen viel zu aufwendig. Es geht auch darum die vielen Strukturen möglichst einfach anlegen zu können, ohne die Länge von Strings "händisch" auszählen zu müssen. Adresse und Länge über alles reicht zum senden. Mit der jetzigen Lösung werden die Strukturen in der übergeordneten Struktur ja richtig angelegt, es ging nur noch um die size bzw. Länge über alles für die Senderoutine, die alles am Stück in einer ISR rausschickt. Mit einer kleinen Schleife gehe ich nun einmal über die strukturen und ermittle diesen fehlenden Wert. Gruß Ingo
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.