在编译时强制两个结构具有相同的大小?

tte*_*ple 11 c embedded

我已经定义了两个数据结构,它们必须保持彼此相同的大小才能使应用程序正常运行.结构用于在PC和DSP之间进行通信.DSP代码位于"C",PC端是C++.

例如:

struct inbound_data{
    int header[5];
    float val1;
    float val2;
    int trailer[3];
};

struct outbound_data{
    int header[5];
    int reply1;
    int reply2;
    float dat1;
    float dat2;
    int filler[1];
}
Run Code Online (Sandbox Code Playgroud)

稍后我会做类似的事情:

int tx_block[sizeof(outbound_data)];
int rx_block[sizeof(inbound_data)];
Run Code Online (Sandbox Code Playgroud)

这些阵列将被传递到通信外围设备,以在设备之间进行发送和接收.

由于硬件的工作原理,两个结构的大小必须匹配,因此缓冲区的大小相同.这很容易确保适当的关注,但偶尔通过设计周期,数据结构会被修改.如果一个人不是非常小心,并且意识到结构保持相同尺寸的要求(并且也反映在PC侧代码中),则会发生混乱.

我想找到一种编译时方法,如果其中一个结构被修改,那么代码就不会构建,这样它就不会与其他结构的大小相匹配.

在"标准"C中,这可能以某种方式在编译时检查大小,如果它们不同则会失败吗?(我认为我的编译器至少是C99,可能不是11).

Sto*_*ica 7

如果你必须使用C99,那么我也喜欢Swordfish会建议一个宏.制作一个可以出现在任何地方并且不会为优化器移除任何对象的方法是将无效数组放入a中typedef.所以一个更通用的静态断言看起来像这样:

#define CONCAT_(A,B) A##B
#define CONCAT(A,B) CONCAT_(A,B)
#define MY_STATIC_ASSERT(p, msg) typedef char CONCAT(dummy__,__LINE__) [(p) ? 1 : -1]
Run Code Online (Sandbox Code Playgroud)

它的设计模仿_Static_assert.传递消息时希望编译器诊断显示它.这里使用它的一个例子.

哪个产生:

main.cpp:4:54: error: size of array 'dummy__13' is negative
 #define MY_STATIC_ASSERT(p, msg) typedef char CONCAT(dummy__,__LINE__) [(p) ? 1 : -1]
                                                      ^~~~~~~
main.cpp:2:22: note: in definition of macro 'CONCAT_'
 #define CONCAT_(A,B) A##B
                      ^
main.cpp:4:47: note: in expansion of macro 'CONCAT'
 #define MY_STATIC_ASSERT(p, msg) typedef char CONCAT(dummy__,__LINE__) [(p) ? 1 : -1]
                                               ^~~~~~
main.cpp:13:1: note: in expansion of macro 'MY_STATIC_ASSERT'
 MY_STATIC_ASSERT(sizeof(struct foo) == sizeof(struct baz), "Do not match!");
Run Code Online (Sandbox Code Playgroud)

在那里你可以看到静态断言和消息.


在事后,你可以改变dummy__please_check_line_意志会产生更多的描述please_check_line_13之上.


Vit*_*meo 6

C11标准添加了一个新关键字_Static_assert.您可以使用它在编译时测试谓词,如果是,则会产生错误false:

_Static_assert(sizeof(outbound_data) == sizeof(inbound_data), "sizes must match");
Run Code Online (Sandbox Code Playgroud)


Lun*_*din 6

强制两个结构在编译时具有相同的大小?

没有标准的方法可以在C中强制执行此操作.只有保护它不发生的方法,例如static_assert- 这可以防止错误代码编译,但不能解决实际问题.

在您的情况下,有几个问题:

  • 你的结构使用了天真的默认类型C.这些不是可移植的,可以有任何大小.这可以很容易通过交换固定intint32_t
  • 无论整数类型如何,Endianess都可能使代码不可移植.这是一个单独的问题,我不会在这里讨论,但需要考虑,特别是对于异国情调的DSP.
  • 任何结构都可以在任何地方包含填充字节,以满足系统特定的对齐要求.问题的根源在于对齐在不同系统上的工作方式不同.这是难以解决的问题.

避免填充的脏修复是static_assert与一些非标准解决方案一起使用,以确保结构具有预期的大小.例如,#pragma pack(1)gcc __attribute__ ((__packed__))等.这些不是标准的,也不是便携式的.此外,在许多系统上跳过填充可能会出现问题,并且您可能会遇到访问错位的问题 - 填充是有原因的.因此,这可能会产生比解决的问题更多的问题.

所以不幸的是,我们最终得到了struct不适合便携式代码的实现.特别适用于数据协议规范等.

如果你需要真正的可移植,坚固的代码,它只剩下一个选项,即使用原始数据数组uint8_t.如果您需要将此数组转换为结构,则必须编写序列化/反序列化代码.这将花费运行时间开销.但如果你想要真正的便携式结构,那就没有别的办法了.