我需要在C预处理器中找到一种方法来测试一个值是否会创建一个0大小的数组

我需要在C预处理器中找到一种方法来测试一个值是否会创建一个0大小的数组,c,preprocessor,C,Preprocessor,我有一个必须填充到64K的结构,以完美地适合嵌入式项目,这样它就填充了一个闪存块。因此,有一个#define,它使用sizeof()将结构中的元素相加,并确定最后的pad[]需要多大,才能使总大小为64K 例如: #define SIZE_OF_MY_PAD (0x10000 - (sizeof(uint16_t) + sizeof(uint8_t)*32 + ... )) typedef struct { uint16_t firstElement; uint8_t secondE

我有一个必须填充到64K的结构,以完美地适合嵌入式项目,这样它就填充了一个闪存块。因此,有一个
#define
,它使用
sizeof()
将结构中的元素相加,并确定最后的
pad[]
需要多大,才能使总大小为64K

例如:

#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];
};
这在很长一段时间内都非常有效,直到突然我们在某些构建配置中根本不需要pad,因为它已经精确到64k了。这会导致代码失败,因为我们的编译器(不是GCC)不允许
pad[0]


我尝试了各种方法来创建一个预处理器值,当检测到该值时,我可以在
#if
语句中使用它,但它总是失败,因为尽管
sizeof()
#define
中是合法的,这在
#中是不合法的,如果

这个问题可以通过C11中引入的匿名结构在不需要任何预处理器的情况下解决

将flash类型定义为包含嵌入匿名结构的成员的联合。使
char\u pad[0x10000]
成为联合的另一个成员,以强制输入类型的总大小

typedef union {
    struct {
        uint16_t firstElement;
        uint8_t  secondElementArray[32];
        float thirdElement;
    };
    char _pad[0x10000];
} flash_t;
此解决方案对结构成员布局的任何修改都具有鲁棒性。此外,这避免了定义C标准技术上禁止的零长度数组的问题(尽管GCC允许)。此外,还可以添加一个静态断言来检查闪存的最大大小是否溢出

示例程序:

#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;
}
编辑
  • 正如评论中所建议的,工会成员
    \u pad
    可以重命名为
    \u rawData
    ,因为
    \u pad
    的语义与问题中的
    pad
    不同

  • 如果需要类型中的成员
    pad
    ,则可以将其作为灵活成员添加到匿名结构的末尾


struct
sizeof
上添加静态断言。您不能在编译时调整填充的大小,但如果不正确,您可以使其在编译时失败。这是一个好问题,但最好在链接器中解决,而不是在编译器中解决。@rd florida您能确定您使用的编译器吗?它与C11兼容吗?@pipe:我不同意。不要让链接器参与进来。您的理智会感谢您的。我建议将此代码放在一个专用部分(
#pragma部分
)中,然后将该部分放在链接器脚本中您想要的位置。为什么
偏移(thirdElement)
36而不是34?@theonlygusti,它取决于编译器。它试图将
float
放在可被4整除的地址。数字36是34之后的第一个。可能也值得同时从
pad
重命名为类似
rawData
的东西-否则,在填充数据结构的其余部分后,用已知数据填充
pad
的代码可能会做完全错误的事情。如果没有匿名结构,它也会工作,除了需要编写
flash.data.firstElement
或其他什么,因为中间结构现在命名为,对吗?@JanDorniak,AFAIK,C标准只要求连续成员的偏移量严格增加,并且第一个成员的偏移量为0。其他一切都是特定于实现的。
offsetof(flash.firstElement) = 0
offsetof(flash.secondElementArray) = 2
offsetof(flash.thirdElement) = 36
sizeof(flash) = 65536
typedef union { struct { ...; uint8_t pad[]; }; char _rawData[0x10000]; } flash_t;