我需要在C预处理器#if中测试一个值是否会创建一个0大小的数组

我有一个结构必须填充到 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];
};

这在很长一段时间内一直有效,直到突然我们在某些构建配置中根本不需要垫,因为它已经正好是 64k。这会导致代码失败,因为我们的编译器(不是 GCC)不允许pad[0].

我曾尝试过各种方法来创建一个预处理价值,我可以在使用#if声明中当检测到,但它总是失败,因为虽然sizeof()是合法的#define,它不是法律#if

回答

借助 C11 中引入的匿名结构,无需任何预处理器即可解决此问题。

将 flash 类型定义为包含嵌入匿名结构的成员的联合。使联合char _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) = %zin", offsetof(flash_t, firstElement));
    printf("offsetof(flash.secondElementArray) = %zin", offsetof(flash_t, secondElementArray));
    printf("offsetof(flash.thirdElement) = %zin", offsetof(flash_t, thirdElement));
    printf("sizeof(flash) = %zin", sizeof flash);
    return 0;
}

产生预期输出:

offsetof(flash.firstElement) = 0
offsetof(flash.secondElementArray) = 2
offsetof(flash.thirdElement) = 36
sizeof(flash) = 65536

编辑

  • 正如评论中所建议的,_pad可以将联合成员重命名为,_rawData因为 的语义_padpad问题中的不同。

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

typedef union { struct { ...; uint8_t pad[]; }; char _rawData[0x10000]; } flash_t;

  • @theonlygusti, it's compiler dependant. It tries to place `float` at the address divisible by 4. Number 36 it the first one after 34.
  • Probably also worth renaming from `pad` to something like `rawData` at the same time - otherwise the code to fill the `pad` with known data after filling in the rest of the data structure might do completely the wrong thing.
  • @JanDorniak, AFAIK, the C standard only requires offsets of consecutive members to be strictly increasing, and that the offset of the first member is 0. Everything else is implementation-specific.

以上是我需要在C预处理器#if中测试一个值是否会创建一个0大小的数组的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>