【问题标题】:C struct paddingC 结构填充
【发布时间】:2010-11-11 07:32:41
【问题描述】:

我可以在各种编译器上使用 pragma pack 来强制结构具有不在其自然对齐上的字段。

这是递归的 - 所以假设 struct typedef A 包含 typedef struct B 的字段。如果使用 pragma 打包 A 是否会强制打包 struct B?

【问题讨论】:

  • @shipshere:不确定它是否对所有编译器都有效,但我认为结构的成员在指定的字节对齐上对齐,或者在它们的自然对齐边界上对齐,以两者中的较小者为准

标签: c


【解决方案1】:

只是不要。如果不指定您正在使用的确切平台/编译器,您甚至无法提出问题,因为丑陋的非标准扩展的确切行为。如果您发现自己需要打包结构,最好找出代码损坏的原因并修复它。打包结构是损坏代码的创可贴,无法解决损坏的根本原因。

【讨论】:

  • @R...:为什么是破解代码的创可贴?打包结构用于内存保护,尤其是在嵌入式系统中。那你什么意思?
  • 有时它们是必要的。想要清楚地表示一个字段在 C 中不是全部字对齐的数据包吗?您可以使用打包的结构或一大堆宏。也就是说,我已经看到了一些与递归打包结构相关的令人讨厌的编译器错误。不寒而栗。
  • @Nathon:将结构用于二进制 IO 已损坏。 #pragma packed 不会修复字节序依赖等。编写适当的序列化函数。 @user384706:在除 x86 之外的任何系统上,读取和写入非对齐字段的额外代码量将远远大于结构中浪费的几个字节 - 特别是如果您修复元素的顺序以便它们自然右对齐,没有填充要求。提示:alignof(type)sizeof(type) 划分为所有类型。
  • 编写一个适当的序列化函数...或者只使用适当的字节序转换函数来确保访问重要字段时的字节序正确性。无偿诉诸权威:__attribute__ ((packed)) 在 Linux 内核头文件中出现 3162 次,在其余源代码中出现 444 次以上。
  • 有时需要使用打包结构来处理内存映射的 I/O 设备。
【解决方案2】:

你不能希望!假设您有一个带有参数 struct A 的函数:

void fn( struct A x ) ;

然后是一个以结构 A 作为成员的打包结构 B:

#pragma pack(1)
struct B
{
    struct A a ; 
}

如果将 B 的成员 a 传递给 fn(),则该函数不知道此实例中的打包,并且会失败。

尽管有其他回答者的结果,我还是在 VC++ 2010 上进行了以下测试:

struct A
{
    int a;
    char b;
    int c ;
} ;

struct B
{
    struct A  d ;
} ;

#pragma pack(1)
struct C
{
    struct A  d ;
} ;


#pragma pack(1)
struct D
{
    int a;
    char b;
    int c ;
} ;

#pragma pack(1)
struct E
{
    struct D ;
} ;

int main()
{
    int a = sizeof(struct A) ;
    int b = sizeof(struct B) ;
    int c = sizeof(struct C) ;
    int d = sizeof(struct D) ;
    int e = sizeof(struct E) ;
}

在调试器yield的main()中检查a、b、c、d和e:

  • a = 12
  • b = 12
  • c = 12
  • d = 9
  • e = 9

struct C 的打包对其结构 A 成员的大小没有影响。

【讨论】:

    【解决方案3】:

    按照 Nathon 的帖子,我在我的电脑上尝试了同样的事情:

    #include <stdio.h>
    
    #if defined PACK_FIRST || defined PACK_BOTH
    #pragma pack(1)
    #endif
    struct inner {
      char a;
      double b;
    };
    
    #if defined PACK_SECOND || defined PACK_BOTH
    #pragma pack(1)
    #endif
    struct outer {
      char a;
      struct inner b;
      char c;
      double d;
    };
    
    int main(void) {
      printf("sizeof (char): %d (1, of course)\n", (int)sizeof (char));
      printf("sizeof (double): %d\n", (int)sizeof (double));
      printf("sizeof (inner): %d\n", (int)sizeof (struct inner));
      printf("sizeof (outer): %d\n", (int)sizeof (struct outer));
      return 0;
    }
    
    $ gcc 4128061.c $ ./a.out sizeof (char): 1 (当然是 1) 大小(双):8 大小(内部):16 尺寸(外):40 $ gcc -DPACK_FIRST 4128061.c $ ./a.out sizeof (char): 1 (当然是 1) 大小(双):8 大小(内部):9 尺寸(外):19 $ gcc -DPACK_SECOND 4128061.c $ ./a.out sizeof (char): 1 (当然是 1) 大小(双):8 大小(内部):16 大小(外部):26 $ gcc -DPACK_BOTH 4128061.c $ ./a.out sizeof (char): 1 (当然是 1) 大小(双):8 大小(内部):9 尺寸(外):19

    显然我的 gcc 从出现 #pragma 行的位置开始打包。

    【讨论】:

    • 第一个 #pragma pack(1) 是否不适用于两个结构,即使由 PACK_FIRST 触发?即你的 ifdef 什么都不做,除非你使用 pragma pack(push,1),然后在第二个结构定义之前使用 pragma(pop)
    【解决方案4】:

    对于我方便的 GCC 版本,看起来是的:

    #include <stdio.h>
    typedef struct
    {
        char a;
        int  b;
    }inner_t;
    #pragma pack(1)
    typedef struct
    {
        char a;
        inner_t inner;
    } outer_t;
    
    int main()
    {
        outer_t outer;
    
        outer.inner.a = 'a';
        outer.inner.b = 0xABCDEF01;
        printf ("outer.inner.a: %c\n", outer.inner.a);
        return 0;
    }
    

    gdb 在 printf 上的中断给了我...

    (gdb) x/5xw &outer
    0x7fffffffe4b0: 0xffff61a0      0xcdef01ff      0x000000ab      0x00000000
    0x7fffffffe4c0: 0x00000000
    

    inner.b 不是字对齐的。因此,在 GCC 4.4.5 下,在 little endian 64 位机器上,如果外部结构被打包,则嵌套结构被打包。请原谅我的 typedef,那些不喜欢它们的人。

    【讨论】:

      猜你喜欢
      • 2014-10-12
      • 2015-04-25
      • 2023-04-03
      • 2017-06-08
      • 2015-09-04
      • 2012-11-14
      • 1970-01-01
      • 1970-01-01
      • 2011-08-13
      相关资源
      最近更新 更多