C结构体填充

3

我可以在不同的编译器上使用“pragma pack”来强制结构体具有不按其自然对齐方式进行对齐的字段。

这个过程是否递归的呢?也就是说,假设结构体typedef A包含typedef struct B的字段。如果使用一个pack pragma对A进行打包,那么会强制打包结构体B吗?


@shipshere:不确定是否对所有编译器都适用,但我认为结构体成员按指定的字节对齐或其自然对齐边界上对齐,两者取较小者。 - Cratylus
4个回答

6

希望你不要这么想!假设你有一个函数,它需要一个参数结构体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) ;
}

在调试器中检查main()函数中的a、b、c、d和e的值,结果如下:

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

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


5

不要使用。丑陋的非标准扩展的确切行为是您无法在未指定您所使用的平台/编译器的情况下提出问题的。如果您发现自己需要压缩结构体,那么最好找出代码存在的问题并加以修复。压缩结构体只是对破损代码的一种权宜之计,对于破损的根本原因没有任何作用。


2
@R...:为什么代码出问题时要使用绷带?紧凑结构在嵌入式系统中特别用于内存保护。所以你是什么意思? - Cratylus
2
有时它们是必要的。想要在C中清晰地表示一个字段不都是字对齐的数据包吗?你可以使用紧凑结构体或者整个一堆宏。尽管如此,我曾经见过一些与递归紧凑结构体相关的令人讨厌的编译器错误。毛骨悚然。 - nmichaels
3
@Nathon说,使用结构体进行二进制输入/输出是错误的。#pragma packed无法解决大小端依赖等问题。应该编写适当的序列化函数。 @user384706说,在除x86以外的任何系统上,读取和写入非对齐字段所需的额外代码量将比在结构体中浪费的几个字节要多得多-特别是如果您调整元素的顺序,使它们自然地对齐而无需填充要求。提示:alignof(type)对于所有类型都会将sizeof(type)除以其结果。 - R.. GitHub STOP HELPING ICE
1
编写一个适当的序列化函数...或者只需使用适当的端序转换函数,在访问重要字段时确保端序正确性。毫无意义的权威呼吁:__attribute__ ((packed)) 在Linux内核头文件中出现了3162次,在其他源代码中出现了444次。 - nmichaels
2
有时需要使用紧凑结构来处理内存映射的I/O设备。 - Darron
显示剩余3条评论

4

根据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 
char类型的大小为:1(当然是1)
double类型的大小为:8
inner结构体的大小为:16
outer结构体的大小为:40
$ gcc -DPACK_FIRST 4128061.c
$ ./a.out 
char类型的大小为:1(当然是1)
double类型的大小为:8
inner结构体的大小为:9
outer结构体的大小为:19
$ gcc -DPACK_SECOND 4128061.c
$ ./a.out 
char类型的大小为:1(当然是1)
double类型的大小为:8
inner结构体的大小为:16
outer结构体的大小为:26
$ gcc -DPACK_BOTH 4128061.c
$ ./a.out 
char类型的大小为:1(当然是1)
double类型的大小为:8
inner结构体的大小为:9
outer结构体的大小为:19

显然,我的gcc从#pragma行出现的地方开始进行打包。


第一个 #pragma pack(1) 不是针对两个结构体吗,即使由 PACK_FIRST 触发?也就是说,除非在第二个结构定义之前使用 pragma pack(push,1),然后再使用 pragma(pop),否则您的 ifdefs 没有任何作用。 - fakeleft

1

根据我手头的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;
}

而且在printf处打断点,gdb会给我...

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

inner.b没有按字对齐。因此,在小端64位机器上的GCC 4.4.5下,如果外部结构是紧凑的,则嵌套结构将被打包。对于那些不喜欢typedef的人,请原谅我。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接