I need a way in a C preprocessor #if to test if a value will create a 0-size array

CPreprocessor

C Problem Overview


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.

C Solutions


Solution 1 - C

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 from pad 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;

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRD FloridaView Question on Stackoverflow
Solution 1 - CtstanislView Answer on Stackoverflow