Est-il possible d'utiliser un sizeof
dans une macro de préprocesseur?
Par exemple, il y a eu une tonne de situations au cours des années dans lesquelles je voulais faire quelque chose comme:
#if sizeof(someThing) != PAGE_SIZE
#error Data structure doesn't match page size
#endif
La chose exacte que je vérifie ici est complètement inventée - le fait est que j’aime souvent utiliser ce type de vérification (taille ou alignement) au moment de la compilation pour éviter que quelqu'un modifie une structure de données, ce qui risquerait taille des choses qui les briseraient.
Inutile de dire que je ne semble pas pouvoir utiliser un sizeof
de la manière décrite ci-dessus.
Est-il possible d'utiliser un "
sizeof
" dans une macro pré-processeur?
Non. Les directives conditionnelles prennent un ensemble restreint d'expressions conditionnelles. sizeof
est une des choses interdites.
Les directives de prétraitement sont évaluées avant que la source ne soit analysée (du moins conceptuellement), de sorte qu'il n'y a pas encore de types ou de variables pour obtenir leur taille.
Cependant, il existe des techniques pour obtenir des assertions de compilation en C (par exemple, voir cette page ).
Il y a plusieurs façons de le faire. Les extraits suivants ne produiront aucun code si sizeof(someThing)
est égal à PAGE_SIZE
; sinon, ils produiront une erreur de compilation.
À partir de C11, vous pouvez utiliser static_assert
(nécessite #include <assert.h>
).
Utilisation:
static_assert(sizeof(someThing) == PAGE_SIZE, "Data structure doesn't match page size");
Si vous voulez juste obtenir une erreur de compilation lorsque sizeof(something)
n'est pas ce que vous attendiez, vous pouvez utiliser la macro suivante:
#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
Utilisation:
BUILD_BUG_ON( sizeof(someThing) != PAGE_SIZE );
Cet article explique en détail pourquoi cela fonctionne.
Sur le compilateur Microsoft C++, vous pouvez utiliser la macro C_ASSERT (nécessite #include <windows.h>
), Qui utilise une astuce similaire à celle décrite dans la section 2.
Utilisation:
C_ASSERT(sizeof(someThing) == PAGE_SIZE);
Je sais que ce fil est vraiment vieux mais ...
Ma solution:
extern char __CHECK__[1/!(<<EXPRESSION THAT SHOULD COME TO ZERO>>)];
Tant que cette expression équivaut à zéro, elle compile bien. Tout le reste et ça explose là. Parce que la variable est extern'd, elle ne prendra pas d'espace, et tant que personne ne la référence (ce qui ne sera pas le cas), cela ne provoquera pas d'erreur de lien.
Pas aussi souple que la macro assert, mais je ne pouvais pas le compiler dans ma version de GCC et cela va ... et je pense que ça compilera à peu près n'importe où.
Je sais que c'est une réponse tardive, mais pour ajouter à la version de Mike, voici une version que nous utilisons qui n'alloue aucune mémoire. Je n'ai pas trouvé le chèque original, je l'ai trouvé sur Internet il y a des années et je ne peux malheureusement pas citer l'auteur. Les deux autres ne sont que des extensions de la même idée.
Parce qu'ils sont typés, rien n'est attribué. Avec __LINE__ dans le nom, il s'agit toujours d'un nom différent, de sorte qu'il peut être copié et collé à la demande. Cela fonctionne dans les compilateurs MS Visual Studio C et les compilateurs GCC Arm. Cela ne fonctionne pas dans CodeWarrior, CW se plaint de la redéfinition, en n'utilisant pas la construction du préprocesseur __LINE__.
//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];
Qu'en est-il de la prochaine macro:
/*
* Simple compile time assertion.
* Example: CT_ASSERT(sizeof foo <= 16, foo_can_not_exceed_16_bytes);
*/
#define CT_ASSERT(exp, message_identifier) \
struct compile_time_assertion { \
char message_identifier : 8 + !(exp); \
}
Par exemple, dans le commentaire, MSVC dit quelque chose comme:
test.c(42) : error C2034: 'foo_can_not_exceed_16_bytes' : type of bit field too small for number of bits
Les réponses existantes montrent simplement comment obtenir l'effet "d'assertions à la compilation" basé sur la taille d'un type. Cela peut répondre aux besoins du PO dans ce cas particulier, mais il existe d'autres cas où vous avez réellement besoin d'un préprocesseur conditionnel basé sur la taille d'un type. Voici comment le faire:
Ecrivez-vous un petit programme en C comme:
/* 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); }
Compiler ça. Ecrivez un script dans votre langage de script favori, qui exécute le programme C ci-dessus et en capture la sortie. Utilisez cette sortie pour générer un fichier d’en-tête en C. Par exemple, si vous utilisiez Ruby, cela pourrait ressembler à:
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
Ajoutez ensuite une règle à votre Makefile ou à un autre script de construction, ce qui lui permettra d’exécuter le script ci-dessus pour construire sizes.h
.
Comprendre sizes.h
partout où vous devez utiliser des conditions de préprocesseur en fonction de la taille.
Terminé!
(Avez-vous déjà tapé ./configure && make
pour construire un programme? Ce que font les scripts configure
est fondamentalement juste comme ci-dessus ...)
#define SIZEOF(x) ((char*)(&(x) + 1) - (char*)&(x))
pourrait fonctionner
À titre de référence pour cette discussion, je signale que certains compilateurs obtiennent le temps avant processeur de sizeof ().
La réponse de JamesMcNellis est correcte, mais certains compilateurs respectent cette limitation (cela viole probablement les règles strictes).
En guise de cas, je me réfère au compilateur IAR C (probablement le premier pour la programmation professionnelle de microcontrôleurs/embarqués).
Dans mon code portable c ++ ( http://www.starmessagesoftware.com/cpcclibrary/ ) je voulais mettre un garde prudent sur la taille de certaines de mes structs ou classes.
Au lieu de trouver un moyen pour le préprocesseur de générer une erreur (qui ne peut pas fonctionner avec sizeof () comme il est indiqué ici), j'ai trouvé ici une solution qui provoque une erreur du compilateur. http://www.barrgroup.com/Embedded-Systems/How-To/C-Fixed-Width-Integers-C99
J'ai dû adapter ce code pour qu'il génère une erreur dans mon compilateur (xcode):
static union
{
char int8_t_incorrect[sizeof( int8_t) == 1 ? 1: -1];
char uint8_t_incorrect[sizeof( uint8_t) == 1 ? 1: -1];
char int16_t_incorrect[sizeof( int16_t) == 2 ? 1: -1];
char uint16_t_incorrect[sizeof(uint16_t) == 2 ? 1: -1];
char int32_t_incorrect[sizeof( int32_t) == 4 ? 1: -1];
char uint32_t_incorrect[sizeof(uint32_t) == 4 ? 1: -1];
};
En C11 _Static_assert
le mot clé est ajouté. Il peut être utilisé comme:
_Static_assert(sizeof(someThing) == PAGE_SIZE, "Data structure doesn't match page size")