我有一个结构必须填充到 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];
};
Run Code Online (Sandbox Code Playgroud)
这在很长一段时间内一直有效,直到突然我们在某些构建配置中根本不需要垫,因为它已经正好是 64k。这会导致代码失败,因为我们的编译器(不是 GCC)不允许pad[0]
.
我曾尝试过各种方法来创建一个预处理价值,我可以在使用#if
声明中当检测到,但它总是失败,因为虽然sizeof()
是合法的#define
,它不是法律#if
。
tst*_*isl 118
借助 C11 中引入的匿名结构,无需任何预处理器即可解决此问题。
将 flash 类型定义为包含嵌入匿名结构的成员的联合。使联合char _pad[0x10000]
的另一个成员强制引入类型的总大小。
typedef union {
struct {
uint16_t firstElement;
uint8_t secondElementArray[32];
float thirdElement;
};
char _pad[0x10000];
} flash_t;
Run Code Online (Sandbox Code Playgroud)
该解决方案对于对结构成员布局的任何修改都是稳健的。此外,这避免了定义零长度数组的问题,该问题在技术上被 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;
}
Run Code Online (Sandbox Code Playgroud)
产生预期输出:
offsetof(flash.firstElement) = 0
offsetof(flash.secondElementArray) = 2
offsetof(flash.thirdElement) = 36
sizeof(flash) = 65536
Run Code Online (Sandbox Code Playgroud)
正如评论中所建议的,_pad
可以将联合成员重命名为,_rawData
因为 的语义_pad
与pad
问题中的不同。
如果pad
需要类型中的成员,则可以将其添加为匿名结构末尾的灵活成员。
typedef union { struct { ...; uint8_t pad[]; }; char _rawData[0x10000]; } flash_t;
Run Code Online (Sandbox Code Playgroud)