C++ #pragma pack的奇怪行为

C++ #pragma pack的奇怪行为,c++,gcc,struct,pragma,gcc4.4,C++,Gcc,Struct,Pragma,Gcc4.4,我在使用#pragma pack时遇到了一个奇怪的问题 我有以下结构: st_a:用#pragma pack(1)包装。大小=1字节。包含位字段 st#u b:用#pragma pack(2)包装。大小=14字节 st#u c:包装有#pragma包装(2)。大小=16字节。包含st_a和st_b st\u d:用#pragma pack(2)包装。大小=16字节。包含st_a和st_b的内容(st_b的成员) 因此,由于st#u a是在#pragma pack(1)下压缩的1字节,并且由于它在

我在使用
#pragma pack
时遇到了一个奇怪的问题

我有以下结构:

  • st_a
    :用
    #pragma pack(1)
    包装。大小=1字节。包含位字段
  • st#u b
    :用
    #pragma pack(2)
    包装。大小=14字节
  • st#u c
    :包装有
    #pragma包装(2)
    。大小=16字节。包含
    st_a
    st_b
  • st\u d
    :用
    #pragma pack(2)
    包装。大小=16字节。包含
    st_a
    st_b
    的内容(
    st_b
    的成员)
  • 因此,由于
    st#u a
    是在
    #pragma pack(1)
    下压缩的1字节,并且由于它在
    st#u c
    内,而st#u c是在
    下压缩的,在
    st_a
    之后的
    st_c
    中应该有一个额外的填充字节,该额外字节后面应该是
    st_b
    的内容,这是一个偶数长度的字符缓冲区(10)

    但是,当我把
    st_b
    的内容拿出来直接放进
    st_a
    时,这件事就很奇怪了。填充显示在字符缓冲区之后,而不是之前(请参见下面的输出)

    有人能解释为什么会发生这种奇怪的行为吗

    代码:

    注意:我使用的是GCC版本4.4.x

    关于输出的一些注意事项

    在第一行中,
    5b
    是在
    4
    之间引入的填充字节,它是1字节
    st_a
    41
    之间,后者是
    st_b
    的缓冲区的第一个字符

    在第二行中,
    0
    是在缓冲区的最后一个字符
    4a
    a
    之间引入的填充字节,后者是
    st\u d
    中字符缓冲区后面的整数


    第三行打印所有结构的大小。

    您没有正确检测到填充。您是否希望填充为零字节?没有理由期望这样。填充字节中的值可以是任何值。 您在末尾看到的零是
    st_b.x1
    的4个字节的一部分。它的值是10,作为一个小的endian 2的补码整数,可以存储为
    0A 00 00

    04 5b 41 42 43 44 45 46 47 48 49 4a 0a 00 00 00
    ^  ^  ^-----------------------------^---------^
    |  |  st_b.buf                      st_b.x1  
    |  random padding byte
    |
    st_a
    
    查看填充的更好方法是使用
    offsetof
    宏。例如:

    #include <stdio.h>
    #include <stddef.h>
    
    #pragma pack(push)
    #pragma pack(1)
    struct Pack1 {
      char x;
    };
    
    #pragma pack(2)
    struct Pack2 {
      short y;
    };
    
    struct Combined {
      struct Pack1 p1;
      struct Pack2 p2;
    };
    #pragma(pop)
    
    int main()
    {
      printf("offset of p1: %u, offset of p2: %u\n",
             offsetof(struct Combined, p1),
             offsetof(struct Combined, p2));
     return 0;
    }
    
    这清楚地表明在
    p1
    p2
    之间有一个填充字节

    将填充视为零的另一种方法是在结构声明之后立即将其所有字节归零:

    st_c one;
    memcpy(&one, 0, sizeof(st_c);
    
    然后,程序的输出将如下所示:

    04 00 41 42 43 44 45 46 47 48 49 4a 0a 00 00 00
    

    当询问编译器扩展时,您应该告诉我们您使用的编译器。@eerorika,我已经更新了我的问题@雷蒙陈,我同意你的看法!你能澄清我的另一个疑问吗?如果
    st_c
    中的缓冲区大小是奇数长度怎么办?在
    st_d
    后面的
    st_a
    中不需要填充,因为下一个字段是可以字节对齐的
    无符号字符。在
    st_a
    之后的
    st_c
    中需要填充,因为下一个字段是标记为需要2字节对齐的
    st_b
    。想象一下,如果有一个新的结构
    st_e
    ,它与
    st_c
    相同,但具有4字节对齐。你不能指望
    st_d
    猜测
    buf1
    x1
    是内联
    st_c
    还是内联
    st_e
    。然后
    st_b
    将是(比如)9字节
    buf
    ,一个填充字节,然后是
    int
    。我知道
    5b
    是填充字节。问题有所不同,请仔细阅读问题。不过,感谢您的努力:-)。我确实学到了一些新东西:
    偏移量
    !是的,我现在明白了。如果你把问题简化到最基本的问题上,那就更清楚了。在您的示例中,有很多额外的代码是不相关的,比如
    st_a
    中的位字段和
    st_b
    中的int。我认为陈雷蒙的评论回答了你的问题。
    offset of p1: 0, offset of p2: 2
    
    st_c one;
    memcpy(&one, 0, sizeof(st_c);
    
    04 00 41 42 43 44 45 46 47 48 49 4a 0a 00 00 00