代码之家  ›  专栏  ›  技术社区  ›  ttemple

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

  •  11
  • ttemple  · 技术社区  · 6 年前

    我定义了两个数据结构,它们必须保持大小相同,才能使应用程序正常运行。该结构用于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];
    }
    

    int tx_block[sizeof(outbound_data)];
    int rx_block[sizeof(inbound_data)];
    

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

    在“standard”C中,是否有可能在编译时检查大小,如果大小不同则会失败(我想我的编译器至少是C99,也许不是11)。

    5 回复  |  直到 6 年前
        1
  •  7
  •   StoryTeller - Unslander Monica    6 年前

    如果你一定要用C99,那我也是 like Swordfish 建议使用宏。使一个数组可以出现在任何地方,并且不会引入任何对象供优化器删除的方法是将无效数组放入 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]
    

    它是为了模仿 _Static_assert here .

    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!");
    

    一直到这里,您都可以看到消息的静态断言。


    事后想想,你可以改变 dummy__ please_check_line_ 将产生更具描述性的 please_check_line_13

        2
  •  6
  •   Vittorio Romeo    6 年前

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

    _Static_assert(sizeof(outbound_data) == sizeof(inbound_data), "sizes must match");
    
        3
  •  6
  •   Lundin    6 年前

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

    在C语言中没有标准的方法来实现这一点。只有办法 static_assert -它阻止了错误代码的编译,但并不能解决实际问题。

    在您的案例中,有几个问题:

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

    静态\u断言 #pragma pack(1) 或gcc __attribute__ ((__packed__)) 这些不是标准的,也不是便携的。此外,跳过填充在许多系统上可能会有问题,并且您可能会遇到访问未对齐的问题—填充是有原因的。因此,这可能会产生比解决问题更多的问题。

    struct 不适用于可移植代码。尤其是数据协议规范。

    如果您需要真正可移植、坚固的代码,那么您只有一个选择,即使用 uint8_t

        4
  •  3
  •   Swordfish    6 年前

    #define C_ASSERT(x, y) { int dummy[(x) == (y) ? 1 : -1]; (void*)dummy; }
    
    struct foo {
        int f;
    };
    
    struct bar {
        int b1;
        //int b2;
    };
    
    int main()
    {
        C_ASSERT(sizeof(struct foo), sizeof(struct bar));
    }
    
        5
  •  1
  •   0___________    6 年前

    struct inbound_data;
    struct outbound_data;
    
    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];
    };
    
    struct inbound_data{
        int header[5];
        float val1;
        float val2;
        int trailer[3];
        char padding[sizeof(struct _inbound_data) < sizeof(struct _outbound_data) ? sizeof(struct _outbound_data) - sizeof(struct _inbound_data) : 0];
    };
    
    struct outbound_data{
        int header[5];
        int reply1;
        int reply2;
        float dat1;
        float dat2;
        int filler[1];
        char padding[sizeof(struct _outbound_data) < sizeof(struct _inbound_data) ? sizeof(struct _inbound_data) - sizeof(struct _outbound_data) : 0];
    };
    

    我当然可以用更短的方式来写,而不需要结构成员的重复——但是我是故意这么做的。

    struct inbound_data1 __attribute__((packed){
        struct _inbound_data id;
        char padding[sizeof(struct _inbound_data) < sizeof(struct _outbound_data) ? sizeof(struct _outbound_data) - sizeof(struct _inbound_data) : 0];
    };
    
    struct outbound_data1 __attribute__((packed){
        struct _outbound_data od;
        char padding[sizeof(struct _outbound_data) < sizeof(struct _inbound_data) ? sizeof(struct _inbound_data) - sizeof(struct _outbound_data) : 0];
    };