Forum: Compiler & IDEs Macro: compiler warning sizeof(any data type)


von Apollo M. (Firma: @home) (majortom)


Lesenswert?

Wenn sizeof(any struct/data type) gebraucht wird und nicht per Hand 
berechnet werden soll, dann kann gcc via warning info das erledigen.

Die Notwendigkeit die Speichergröße von komplexenn Strukturen zu kennen 
war Impuls in stackoverflow, ... zu suchen.
1
//get warning at compile time about sizeof(any data tpye)
2
enum {_VAL_=-4242} _cv_;
3
#define mSizeOfWarning(type) void type##size(){switch(_cv_){case sizeof(type):break;}}

example string fw id:
1
//firmware id stored in eeprom
2
#define BUILD_MAJOR 1
3
#define BUILD_MINOR 2
4
#define BUILD_PATCH 0
5
#define VERSION STRG(BUILD_MAJOR) "." STRG(BUILD_MINOR)"."STRG(BUILD_PATCH)
6
#define SWID ("[ver:"VERSION"]-["__TIME__ " " __DATE__"]")
7
8
mSizeOfWarning(SWID);

output at compile time:
Warning  case value '35' not in enumerated type [-Wswitch]

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Apollo M. schrieb:
> Die Notwendigkeit die Speichergröße von komplexenn Strukturen zu kennen
> war Impuls in stackoverflow, ... zu suchen.

Meist brauche ich die nur im Code. Und wenn nicht, schreib ich mir ein 
kleines Programm, das sie mir per printf ausgibt. Warum sollte ich dazu 
Warnungen missbrauchen?

von Bauform B. (bauformb)


Lesenswert?

Schreibt man nicht lieber so?
1
static const char swid[] = "[ver:1.2.10]-["__TIME__" "__DATE__"]";
weil, dann geht's so:
1
(gdb) print swid
2
$1 = "[ver:1.2.10]-[08:16:33 Jun 11 2022]"
3
(gdb) print sizeof swid
4
$2 = 36
im Gegensatz zu
1
(gdb) print sizeof SWID
2
No symbol "SWID" in current context.

Damit es garnicht erst soweit kommt, benutze ich gerne
1
_Static_assert  (sizeof(swid) == 35, "zu oft gepatched");
1
main.c:13:1: error: static assertion failed: "zu oft gepatched"
2
   13 | _Static_assert  (sizeof(swid) == 35, "zu oft gepatched");

von A. S. (Gast)


Lesenswert?

Bauform B. schrieb:
> _Static_assert

Auch Compile-tume-assert genannt. Gibt es auch in C selbstgemacht, auch 
in alten Varianten.

Static-asserts sollte jeder kennen und bei Bedarf nutzen.

von Hannes J. (Firma: _⌨_) (pnuebergang)


Lesenswert?

Apollo M. schrieb:
> Wenn sizeof(any struct/data type) gebraucht wird und nicht per Hand
> berechnet werden soll,

Dann hat man die Kontrolle über sein Leben verloren.

Wenn du exakte Kontrolle über eine Datenstruktur haben musst, dann baue 
sie byteweise (notfalls sogar bitweise) zusammen. Das nennt sich 
marshalling. Proaktiv statt reaktiv.

Ansonsten steht man früher oder später mit runtergelassener Hose da, 
weil man solche Dinge wie "wie groß ist der Datensatz wirklich und wie 
ist das Encoding" nur mit "was der Compiler halt eben gerade heute, bei 
aktueller Mondphase draus macht" beantworten kann.

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

Die Discussion zu dem Thema in stackoverflow ist umfangreich, also  gibt 
es - wie für mich auch - im Ausnahmefall wohl Sinn/Nutzen.

Der Hintergrund zu dem Thema ...

Eine struct soll ans Ende im eeprom plaziert werden - was vermutlich 
eleganter mit linked extra Segment geht ... Aber wie genau (avr-gcc, 
as7)?

Dazu definiere ich die struct in der size des eeprom und plaziere ein 
unused array mit size E2END (end addr eeprom) minus size der struct ee 
Elemente, die ich aber nicht via sizeof bestimmen kann.
Ohne unused array wird mit mSizeOfWarning(ee) die size of struct ee zur 
Compilierzeit angezeigt und kann dann für die unused array size XXX 
genutzt werden.

1
EEMEM struct ee_t {
2
   uint8_t
3
   unused[E2END+1-(XXX+sizeof(SWID)-1)]
4
   oscCal[12],    
5
   TimerOperation,
6
   TimeMode,
7
   ...
8
   swID[sizeof(SWID)-1];
9
} ee = {...};

Ideen/Vorschläge? sind willkommen!

: Bearbeitet durch User
von Michael D. (nospam2000)


Lesenswert?

Apollo M. schrieb:
> EEMEM struct ee_t {
>    uint8_t
>    unused[E2END+1-(XXX+sizeof(SWID)-1)]
>    oscCal[12],
>    TimerOperation,

Es wurde oben schon geschrieben, dass man bei sowas auf das alignment 
bzw. padding achten muss um nicht mit einem anderen Compiler oder auch 
nur geänderten Compiler Optionen Überraschungen zu erleben. Nicht nur 
die Größe ist wichtig, jeder einzelne Offset ist entscheidend.

Grundsätzlich bedeutet dies für solche Strukturen das alignment fest zu 
definieren, siehe z.B. hier: 
https://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/Structure_002dPacking-Pragmas.html

Noch besser ist es, wenn man die Struktur selbst so definiert, dass das 
alignment des Compilers egal ist, d.h. man sortiert die Reihenfolge
der Member entsprechend und fügt ggf. händisch padding bytes ein.

 Michael

von Bauform B. (bauformb)


Lesenswert?

Vielleicht so ähnlich:
1
#define E2END 512
2
3
typedef struct ee_data_t {
4
   uint8_t oscCal[12];
5
   uint8_t TimerOperation;
6
   uint8_t TimerMode;
7
   uint8_t swid[36];
8
} ee_data_t;
9
10
typedef struct ee_t {
11
   uint8_t   unused[E2END+1-sizeof(ee_data_t)-1];
12
   ee_data_t ee_data;
13
} ee_t;

von A. S. (Gast)


Lesenswert?

Apollo M. schrieb:
> Dazu definiere ich die struct in der size des eeprom und plaziere ein
> unused array mit size E2END (end addr eeprom) minus size der struct ee
> Elemente, die ich aber nicht via sizeof bestimmen kann.
> Ohne unused array wird mit mSizeOfWarning(ee) die size of struct ee zur
> Compilierzeit angezeigt und kann dann für die unused array size XXX
> genutzt werden.

Das dir die Größe bei der Warnung angezeigt wird, ist toll. Wenn ich 
sowas gebraucht habe, habe ich manchmal mehrmals compiliert mit 
static-asserts oder hab das Programm extra dafür laufen gelassen (mit 
Ausgabe des Wertes, wo auch immer, Debugger, LED, printf, Display, ...). 
Oder hab im Output nachgesehen. Alles recht aufwendig und hat teils 
Minuten gedauert.

Vermutlich erinnere ich mich beim nächsten Mal an diese Möglichkeit und 
freue mich, dass Du es damit einfach hast :-)

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

A. S. schrieb:
> Alles recht aufwendig und hat teils Minuten gedauert.

So ist es, das war auch immer mein Problem.

Es werden verschiedene Methoden hins. entspr. Warning Info im Netz 
diskutiert, die nicht bei jedem Compiler gleich gut funktionieren - 
Warning kommt fast immer, aber ohen die Angabe des gesuchten Wertes. Bei 
dem Macro hier spiel die -4242 eine unklare Rolle, +-1, +-100, ... geht 
alles nicht - warum auch immer - aber >256 bringt das Warning mit Angabe 
der gesuchten Zahl.

: Bearbeitet durch User
von Apollo M. (Firma: @home) (majortom)


Lesenswert?

Zur Info E2END (end address eeprom) liefert das Device Package und ist 
dem Compiler bekannt.

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

Ob/wie das mit einer extra section zum linken gehen könnte ist mir 
unklar, da die Linker section start address über E2END+2-sizeof(ee_t) 
berechnet werden muss.

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

Das eeprom data address location Problem via linker section entzieht 
sich noch einer guten Antwort ...

https://stackoverflow.com/questions/70312593/avr-gcc-how-to-use-attribute-address-with-eemem

von Bauform B. (bauformb)


Lesenswert?

Apollo M. schrieb:
> Ob/wie das mit einer extra section zum linken gehen könnte ist mir
> unklar, da die Linker section start address über E2END+2-sizeof(ee_t)
> berechnet werden muss.

Warum? Die Größe der struct hast du doch selbst festgelegt:

Apollo M. schrieb:
> Dazu definiere ich die struct in der size des eeprom

Wenn du für diese struct eine eigene section ganz normal (also auf eine 
feste Startadresse) im Linkerscript anlegst, ist doch die Endadresse 
automatisch durch die Größe gegeben. Ganz ohne weitere Berechnungen.

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

Bauform B. schrieb:
> Wenn du für diese struct eine eigene section ganz normal (also auf eine
> feste Startadresse) im Linkerscript anlegst, ist doch die Endadresse
> automatisch durch die Größe gegeben. Ganz ohne weitere Berechnungen.

Die struct in der size des eeprom ist nur eine Hilfslösung, weil nicht 
klar ist wie es sonst gehen könnte.

Entgegen der Festlegung über die Startadresse soll über die Endadresse 
und size of data object die Plazierung im eeprom erfolgen.

Wahrscheinlich unrealistisch ...

von Bauform B. (bauformb)


Lesenswert?

Na gut, du hast es so gewollt ;) ee_t enthält jetzt nur noch Nutzdaten, 
kein unused[] mehr. Die letzte Adresse im EEPROM ist bekannt. Man 
definiert eine section mit dieser Adresse+1 als Start. Die struct ee_t 
ee wird in die neue section gelinkt, also hinter das EEPROM. Dann 
nimmt man einen Pointer auf diese struct und dekrementiert den einmal. 
Damit zeigt er auf eine ee_t struct im EEPROM und die endet genau auf 
der letzten Adresse.

So muss niemand jemals die Größe von irgendwas erfahren, alles passt 
ganz von alleine zusammen. Trotzdem würde ich die section genau so groß 
machen wie das EEPROM. Einmal zwecks der Schönheit und dann zwecks der 
Warnung, falls ee_t irgendwann zu groß wird.

Mit dem Alignment muss man trotzdem aufpassen, falls mal größere 
Datentypen als uint8_t gebraucht werden. Es wird zwar immer 
funktionieren, weil die section ja auf deutlich mehr als 4 aligned ist. 
Aber evt. bleiben am Ende 1 bis ein paar Byte "Luft".

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

Bauform B. schrieb:
> Na gut, du hast es so gewollt ;)

Das muss ich erstmal durchdenken und ausprobieren - blicke es noch nicht 
wirklich.

Bauform B. schrieb:
> Einmal zwecks der Schönheit und dann zwecks der Warnung,

Ja, schön ist die Lösung mit unused[] nicht und zwickt, also nicht im 
Sinne "pythonic coding style". :-)

Ich bin Python Anhänger und habe am Anfang mit Python eher C coding 
style programmiert mit Python Syntax - full ugly.

von dfIas (Gast)


Angehängte Dateien:

Lesenswert?

Gibt es außer Visual Studio eigentlich keine weitere IDE, die mir beim 
Hovern über ein 'sizeof' die Größe im Editor anzeigt? Bei Eclipse & Co. 
tut sich da leider nichts. :(

von Rolf M. (rmagnus)


Lesenswert?

Mein vim mit ycm tut das auch.

von Markus F. (mfro)


Lesenswert?

Bei solchen Dingen wär' ich vorsichtig.

Ich weiß nicht, wie IntelliSense das macht, aber zumindest die 
OSS-Implementierungen verwenden (meist?) einen Language-Server im 
Hintergrund und das LSP-Protokoll, um von dort Namen- und 
Typinformationen zu bekommen.

Soweit ich das kenne, sind diese Language-Server (zumindest für C/C++) 
meist clang-basiert.

Wenn man nun (z.B.) mit gcc compiliert (möglicherweise mit 
Compiler-Optionen die die clang-Lib im LS gar nicht zu sehen bekommt, 
weil sie im Makefile versteckt sind) wird da zwar was angezeigt, das 
muss aber nicht unbedingt stimmen.

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.