Wie kann ich “sizeof” in einem Präprozessormakro verwenden?
Lesezeit: 8 Minuten
Brad
Gibt es eine Möglichkeit, a sizeof in einem Präprozessor-Makro?
Zum Beispiel gab es im Laufe der Jahre eine Menge Situationen, in denen ich so etwas tun wollte:
#if sizeof(someThing) != PAGE_SIZE
#error Data structure doesn't match page size
#endif
Das genaue, was ich hier überprüfe, ist vollständig erfunden – der Punkt ist, dass ich oft gerne diese Art von (Größe oder Ausrichtung) Kompilierzeitprüfungen einfüge, um zu verhindern, dass jemand eine Datenstruktur ändert, die falsch ausgerichtet oder neu ausgerichtet werden könnte. Größe Dinge, die sie zerbrechen würden.
Unnötig zu sagen – ich scheine nicht in der Lage zu sein, a zu verwenden sizeof in der oben beschriebenen Weise.
Genau aus diesem Grund gibt es Build-Systeme.
– Simon Toth
2. November 2010 um 15:36 Uhr
Das ist genau der Grund, warum #error-Anweisungen immer in doppelten Anführungszeichen stehen sollten (unterbrochene Zeichenkonstante wegen „nicht“).
– Jens
31. August 2015 um 9:01 Uhr
Hallo @Brad. Bitte erwägen Sie, Ihre akzeptierte Antwort in die Antwort von nevermind zu ändern, da die derzeit akzeptierte Antwort in der Zwischenzeit etwas veraltet ist.
Dazu gibt es mehrere Möglichkeiten. Die folgenden Ausschnitte erzeugen keinen Code, wenn sizeof(someThing) gleich PAGE_SIZE; andernfalls erzeugen sie einen Kompilierzeitfehler.
1. C11-Weg
Ab C11 können Sie verwenden static_assert (erfordert #include <assert.h>).
Verwendungszweck:
static_assert(sizeof(someThing) == PAGE_SIZE, "Data structure doesn't match page size");
2. Benutzerdefiniertes Makro
Wenn Sie nur einen Kompilierungsfehler erhalten möchten, wenn sizeof(something) nicht das ist, was Sie erwarten, können Sie folgendes Makro verwenden:
Dieser Artikel erklärt ausführlich, warum es funktioniert.
3. MS-spezifisch
Auf Microsoft C++ Compiler können Sie verwenden C_ASSERT Makro (erfordert #include <windows.h>), die einen ähnlichen Trick wie den in Abschnitt 2 beschriebenen verwendet.
Verwendungszweck:
C_ASSERT(sizeof(someThing) == PAGE_SIZE);
…das ist verrückt. Warum ist dies nicht die akzeptierte Antwort, @Brad (OP)?
– Techniker
15. September 2015 um 14:12 Uhr
Schöner Hinweis auf BUILD_BUG_ON.
– Petr Vepřek
12. Juli 2016 um 7:30 Uhr
Das Makro funktioniert nicht in GNU gcc (getestet mit Version 4.8.4) (Linux). Bei der ((void)sizeof(... es Fehler mit expected identifier or '(' before 'void' und expected ')' before 'sizeof'. Aber grundsätzlich size_t x = (sizeof(... funktioniert stattdessen wie beabsichtigt. Sie müssen das Ergebnis irgendwie “verwenden”. Damit dies aufgerufen werden kann mehrmals entweder innerhalb einer Funktion oder im globalen Bereich, so etwas wie extern char _BUILD_BUG_ON_ [ (sizeof(...) ]; kann wiederholt verwendet werden (keine Nebenwirkungen, nicht wirklich referenzieren _BUILD_BUG_ON_ irgendwo).
– JonBrave
2. Januar 2017 um 12:23 Uhr
@Engineer: Weil die Frage bereits 2010 gestellt und beantwortet und zu diesem Zeitpunkt akzeptiert wurde, während DIESE Antwort 2013 gepostet wurde. Sie können eine Antwort später nicht “ablehnen” (zumindest meines Wissens), also Die jetzt bessere Antwort ist nicht die akzeptierte.
– Bodo Thiesen
12. Oktober 2019 um 7:07 Uhr
@Engineer schau, der Wahnsinn hat aufgehört 😉
– Bodo Thiesen
17. Oktober 2019 um 19:11 Uhr
Gibt es trotzdem eine “sizeof” in einem Präprozessor-Makro?
Nein. Die bedingten Direktiven akzeptieren eine eingeschränkte Menge bedingter Ausdrücke; sizeof ist eines der Dinge, die nicht erlaubt sind.
Vorverarbeitungsanweisungen werden ausgewertet, bevor die Quelle (zumindest konzeptionell) analysiert wird, sodass noch keine Typen oder Variablen vorhanden sind, die ihre Größe erhalten müssen.
Es gibt jedoch Techniken, um Assertions zur Kompilierzeit in C zu erhalten (siehe zum Beispiel diese Seite).
Toller Artikel – clevere Lösung! Obwohl Sie sich administrieren müssen – sie haben die C-Syntax wirklich an ihre Grenzen gebracht, um diese zum Laufen zu bringen! :-Ö
– Brad
2. November 2010 um 16:21 Uhr
Es stellt sich heraus – wie der Artikel sogar sagt – ich baue gerade Linux-Kernel-Code – und es gibt bereits eine Definition im Kernel – BUILD_BUG_ON – wo der Kernel sie für Dinge verwendet wie: BUILD_BUG_ON(sizeof(char) != 8)
– Brad
2. November 2010 um 16:30 Uhr
@Brad BUILD_BUG_ON und andere generieren sicherlich falschen Code, der nicht kompiliert werden kann (und während des Prozesses eine nicht offensichtliche Fehlermeldung ausgibt). Nicht wirklich #if-Anweisung, daher können Sie beispielsweise keinen Codeblock basierend darauf ausschließen.
– Kelter
14. Oktober 2013 um 8:21 Uhr
Ich weiß, es ist eine späte Antwort, aber um Mikes Version hinzuzufügen, verwenden wir hier eine Version, die keinen Speicher zuweist. Den Originalgrößencheck habe ich mir nicht ausgedacht, ich habe ihn vor Jahren im Internet gefunden und kann den Autor leider nicht nennen. Die anderen beiden sind nur Erweiterungen derselben Idee.
Da es sich um Typedefs handelt, wird nichts zugewiesen. Mit der __LINE__ im Namen ist es immer ein anderer Name, sodass er kopiert und bei Bedarf eingefügt werden kann. Dies funktioniert in MS Visual Studio C-Compilern und GCC-Arm-Compilern. Es funktioniert nicht in CodeWarrior, CW beschwert sich über die Neudefinition, die das Präprozessorkonstrukt __LINE__ nicht verwendet.
//Check overall structure size
typedef char p__LINE__[ (sizeof(PARS) == 4184) ? 1 : -1];
//check 8 byte alignment for flash write or similar
typedef char p__LINE__[ ((sizeof(PARS) % 8) == 0) ? 1 : 1];
//check offset in structure to ensure a piece didn't move
typedef char p__LINE__[ (offsetof(PARS, SUB_PARS) == 912) ? 1 : -1];
Das funktioniert wirklich gut für ein Standard-C-Projekt … Ich mag es!
– Ashley Duncan
20. Februar 2018 um 8:55 Uhr
Dies sollte aufgrund der Nullzuordnung die richtige Antwort sein. Noch besser in eine Definition: #define STATIC_ASSERT(condition) typedef char p__LINE__[ (condition) ? 1 : -1];
– Renaud Cerrato
25. Juli 2018 um 7:41 Uhr
p__LINE__ erzeugt keinen eindeutigen Namen. Es erzeugt p__LINE__ als Variable. Sie benötigen ein preproc-Makro und verwenden __CONCAT aus sys/cdefs.h .
– Koroos
2. März 2020 um 11:37 Uhr
Scott
Ich weiß, dieser Thread ist wirklich alt, aber…
Meine Lösung:
extern char __CHECK__[1/!(<<EXPRESSION THAT SHOULD COME TO ZERO>>)];
Solange dieser Ausdruck gleich Null ist, wird er gut kompiliert. Alles andere und es explodiert genau dort. Da die Variable extern ist, nimmt sie keinen Platz ein, und solange niemand darauf verweist (was nicht der Fall ist), verursacht sie keinen Link-Fehler.
Nicht so flexibel wie das Assert-Makro, aber ich konnte das nicht in meiner Version von GCC kompilieren, und das wird … und ich denke, es wird fast überall kompiliert.
Die vorhandenen Antworten zeigen nur, wie Sie den Effekt von “Compile-Time-Assertionen” basierend auf der Größe eines Typs erzielen können. Das mag in diesem speziellen Fall die Anforderungen des OP erfüllen, aber es gibt andere Fälle, in denen Sie wirklich einen Präprozessor benötigen, der auf der Größe eines Typs basiert. So geht’s:
Schreiben Sie sich ein kleines C-Programm wie:
/* you could call this sizeof_int.c if you like... */
#include <stdio.h>
/* 'int' is just an example, it could be any other type */
int main(void) { printf("%zd", sizeof(int); }
Kompilieren Sie das. Schreiben Sie ein Skript in Ihrer bevorzugten Skriptsprache, das das obige C-Programm ausführt und dessen Ausgabe erfasst. Verwenden Sie diese Ausgabe, um eine C-Header-Datei zu generieren. Wenn Sie beispielsweise Ruby verwenden, könnte es so aussehen:
sizeof_int = `./sizeof_int`
File.open('include/sizes.h','w') { |f| f.write(<<HEADER) }
/* COMPUTER-GENERATED, DO NOT EDIT BY HAND! */
#define SIZEOF_INT #{sizeof_int}
/* others can go here... */
HEADER
Fügen Sie dann eine Regel zu Ihrem Makefile oder einem anderen Build-Skript hinzu, wodurch das obige Skript zum Erstellen ausgeführt wird sizes.h.
Enthalten sizes.h überall dort, wo Sie Präprozessor-Bedingungen basierend auf Größen verwenden müssen.
Fertig!
(Haben Sie schon einmal getippt ./configure && make um ein Programm zu erstellen? Was configure Skripte tun, ist im Grunde genau wie oben …)
Ähnlich verhält es sich, wenn Sie Tools wie “autoconf” verwenden.
test.c(42) : error C2034: 'foo_can_not_exceed_16_bytes' : type of bit field too small for number of bits
Ähnlich verhält es sich, wenn Sie Tools wie “autoconf” verwenden.
– Alexander Stöhr
14. Januar 2019 um 13:44 Uhr
graziano gouvernatori
Nur als Referenz für diese Diskussion berichte ich, dass einige Compiler sizeof() in der Präprozessorzeit erhalten.
Die Antwort von James McNellis ist richtig, aber einige Compiler gehen diese Einschränkung durch (dies verstößt wahrscheinlich gegen striktes Ansi c).
In diesem Fall beziehe ich mich auf den IAR C-Compiler (wahrscheinlich der führende für professionelle Mikrocontroller-/Embedded-Programmierung).
Bist du dir da sicher? IAR Ansprüche dass ihre Compiler den ISO C90- und C99-Standards entsprechen, die keine Bewertung zulassen sizeof zur Vorverarbeitungszeit. sizeof sollte nur als Identifikator behandelt werden.
– Keith Thompson
29. August 2013 um 14:58 Uhr
1998 schrieb jemand in der Newsgroup comp.std.c: „Es war schön damals, als Dinge wie #if (sizeof(int) == 8) hat tatsächlich funktioniert (auf einigen Compilern).” Die Antwort: “Muss vor meiner Zeit gewesen sein.”, war von Dennis Ritchie.
– Keith Thompson
29. August 2013 um 15:01 Uhr
Entschuldigung für die späte Antwort … Ja, ich bin sicher, ich habe funktionierende Codebeispiele, die für 8/16/32-Bit-Mikrocontroller und Renesas-Compiler (sowohl R8 als auch RX) kompiliert wurden.
– graziano gouvernatori
26. September 2013 um 17:23 Uhr
Eigentlich sollte es eine Option geben, “strenge” ISO C zu verlangen
– graziano gouvernatori
26. September 2013 um 17:24 Uhr
Es ist kein Verstoß gegen den Standard, solange der Standard es nicht verbietet. dann würde ich es eine seltene und nicht standardmäßige Funktion nennen – daher werden Sie es in regelmäßigen Fällen vermeiden, um die Unabhängigkeit des Compilers und die Portabilität der Plattform zu wahren.
– Alexander Stöhr
14. Januar 2019 um 13:46 Uhr
14220200cookie-checkWie kann ich “sizeof” in einem Präprozessormakro verwenden?yes
Genau aus diesem Grund gibt es Build-Systeme.
– Simon Toth
2. November 2010 um 15:36 Uhr
Das ist genau der Grund, warum #error-Anweisungen immer in doppelten Anführungszeichen stehen sollten (unterbrochene Zeichenkonstante wegen „nicht“).
– Jens
31. August 2015 um 9:01 Uhr
Hallo @Brad. Bitte erwägen Sie, Ihre akzeptierte Antwort in die Antwort von nevermind zu ändern, da die derzeit akzeptierte Antwort in der Zwischenzeit etwas veraltet ist.
– Bodo Thiesen
13. Oktober 2019 um 7:46 Uhr
@BodoThiesen Fertig.
– Brad
16. Oktober 2019 um 18:32 Uhr
Sieht so aus, als ob die ganze Frage und die Antworten dupliziert wurden newbedev.com/how-can-i-use-sizeof-in-a-preprocessor-macro.
– CristiFati
29. Juli 2021 um 21:24 Uhr