Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c

preprocessor

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.

like image 689
RD Florida Avatar asked Apr 07 '21 19:04

RD Florida


People also ask

What is the need for preprocessor in C?

The C preprocessor is a macro processor that is used automatically by the C compiler to transform your program before actual compilation. It is called a macro processor because it allows you to define macros, which are brief abbreviations for longer constructs.

What does ## mean in C preprocessor?

The double-number-sign or token-pasting operator (##), which is sometimes called the merging or combining operator, is used in both object-like and function-like macros. It permits separate tokens to be joined into a single token, and therefore, can't be the first or last token in the macro definition.

What is preprocessor in C with example?

The C preprocessor is a macro preprocessor (allows you to define macros) that transforms your program before it is compiled. These transformations can be the inclusion of header files, macro expansions, etc. All preprocessing directives begin with a # symbol. For example, #define PI 3.14.

How does a preprocessor work in C?

The preprocessor provides the ability for the inclusion of header files, macro expansions, conditional compilation, and line control. In many C implementations, it is a separate program invoked by the compiler as the first part of translation.


1 Answers

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; 
like image 51
tstanisl Avatar answered Sep 23 '22 08:09

tstanisl