web-dev-qa-db-fra.com

Comment utiliser "sizeof" dans une macro de préprocesseur?

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.

81
Brad

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 ).

68
James McNellis

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.

1. manière C11

À 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");

2. Macro personnalisé

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.

3. spécifique à MS

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);
55
nevermind

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ù.

9
Scott

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];
7
Paul

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
4
Sergio

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 ...)

3
Alex D

#define SIZEOF(x) ((char*)(&(x) + 1) - (char*)&(x)) pourrait fonctionner

1
user2560622

À 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).

1

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];
};
0
Mike

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")
0
cagatayo