I need a way in a C preprocessor #if to test if a value will create a 0-size array
I have a structure that must pad out to 64K to fit perfectly in an embedded project, so that it fills out a flash block. So there is a #define
that adds up the elements in the structure using sizeof()
and determines how big the pad[]
at the end needs to be to cause the total size to be 64K.
For example :
#define SIZE_OF_MY_PAD (0x10000 - (sizeof(uint16_t) + sizeof(uint8_t)*32 + ... ))
typedef struct {
uint16_t firstElement;
uint8_t secondElementArray[32];
...
uint8_t pad[SIZE_OF_MY_PAD];
};
This has worked great for a long time until suddenly we don't need the pad at all in certain build configurations because it is already exactly 64k. This causes the code to fail because our compiler (not GCC) does not allow pad[0]
.
I have tried various ways to create a preprocessor value that I can use in an #if
statement when this is detected, but it always fails because although sizeof()
is legal in #define
, it is not legal in #if
.
Solution 1:
This problem can be solved without any need for preprocessor with a help of anonymous structs introduced in C11.
Define the flash type as a union that contains members embedded into anonymous struct. Make char _pad[0x10000]
the other member of the union to force the total size of the introduced type.
typedef union {
struct {
uint16_t firstElement;
uint8_t secondElementArray[32];
float thirdElement;
};
char _pad[0x10000];
} flash_t;
This solution is robust to any modifications to the layout of the struct members. Moreover, this avoids problem of defining zero-length array that is technically forbidden by C standard (though allowed in GCC). Additionally, one can add a static assert to check if the maximal size of the flash got overflown.
Example program:
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
typedef union {
struct {
uint16_t firstElement;
uint8_t secondElementArray[32];
float thirdElement;
// int kaboom[20000]; // will trigger assert if uncommented
};
char _pad[0x10000];
} flash_t;
_Static_assert(sizeof(flash_t) == 0x10000, "Oops, flash_t got too large");
int main() {
flash_t flash;
printf("offsetof(flash.firstElement) = %zi\n", offsetof(flash_t, firstElement));
printf("offsetof(flash.secondElementArray) = %zi\n", offsetof(flash_t, secondElementArray));
printf("offsetof(flash.thirdElement) = %zi\n", offsetof(flash_t, thirdElement));
printf("sizeof(flash) = %zi\n", sizeof flash);
return 0;
}
Produces expected output:
offsetof(flash.firstElement) = 0
offsetof(flash.secondElementArray) = 2
offsetof(flash.thirdElement) = 36
sizeof(flash) = 65536
EDIT
-
As suggested in the comment the union member
_pad
could be renamed to_rawData
because semantics of_pad
differs frompad
in the question. -
If a member
pad
within the type is required then one could add it as a flexible member at the end of anonymous struct.
typedef union { struct { ...; uint8_t pad[]; }; char _rawData[0x10000]; } flash_t;