【发布时间】:2019-07-11 13:48:23
【问题描述】:
我在使用#pragma pack 时遇到了一个奇怪的问题。
我有以下结构:
-
st_a:挤满了#pragma pack(1)。大小 = 1 个字节。包含位域。 -
st_b:挤满了#pragma pack(2)。大小 = 14 字节。 -
st_c:挤满了#pragma pack(2)。大小 = 16 字节。包含st_a和st_b -
st_d:挤满了#pragma pack(2)。大小 = 16 字节。包含st_a和st_b的内容(st_b的成员)
因此,由于st_a 是在#pragma pack(1) 下打包的 1 个字节,并且由于它在st_c 内部,而#pragma pack(2) 打包在st_c 中,紧跟在@987654340 之后应该有一个额外的填充字节@ 并且那个额外的字节后面应该跟 st_b 的内容,这是一个偶数长度 (10) 的字符缓冲区。
但是,当我将st_b 的内容取出并直接放入st_a 时,这件事就奇怪了。填充出现在字符缓冲区之后,而不是之前(见下面的输出)。
有人能解释一下,为什么会出现这种奇怪的行为吗?
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
#pragma pack(push)
#pragma pack(1)
typedef struct A {
int a : 1;
int b : 1;
int c : 1;
int d : 1;
int e : 1;
int f : 1;
int g : 1;
int h : 1;
} st_a;
#pragma pack(pop)
#pragma pack(push)
#pragma pack(2)
typedef struct B {
unsigned char buf[10];
int x;
} st_b;
typedef struct C {
st_a temp1;
st_b temp2;
} st_c;
typedef struct D {
st_a temp3;
unsigned char buf1[10];
int x1;
} st_d;
#pragma pack(pop)
void print_hex(unsigned char* packet) {
for (int i = 0; i < 16; i++) {
printf("%x ", packet[i]);
} printf("\n");
}
int main() {
st_c one;
one.temp1.a = 0;
one.temp1.b = 0;
one.temp1.c = 1;
one.temp1.d = 0;
one.temp1.e = 0;
one.temp1.f = 0;
one.temp1.g = 0;
one.temp1.h = 0;
memcpy(&one.temp2.buf, "ABCDEFGHIJ", 10);
one.temp2.x = 10;
st_d two;
two.temp3.a = 0;
two.temp3.b = 0;
two.temp3.c = 1;
two.temp3.d = 0;
two.temp3.e = 0;
two.temp3.f = 0;
two.temp3.g = 0;
two.temp3.h = 0;
memcpy(&two.buf1, "ABCDEFGHIJ", 10);
two.x1 = 10;
print_hex((unsigned char*) &one);
print_hex((unsigned char*) &two);
cout << sizeof(st_c) << " " << sizeof(st_a) << " " << sizeof(st_b) << " " << sizeof(st_d) << endl;
return 0;
}
输出:
4 5b 41 42 43 44 45 46 47 48 49 4a a 0 0 0
4 41 42 43 44 45 46 47 48 49 4a 0 a 0 0 0
16 1 14 16
注意:我使用的是 GCC 版本 4.4.x。
关于输出的一些说明:
在第一行,5b 是在4 之间引入的填充字节,这是一个字节st_a 和41,它是st_b 的缓冲区的第一个字符。
在第二行中,0 是在缓冲区的最后一个字符 4a 和 a 之间引入的填充字节,st_d 中的字符缓冲区后面的整数。
第三行打印所有结构的大小。
【问题讨论】:
-
在询问编译器扩展时,您应该告诉我们您使用的编译器。
-
@eerorika,我已经更新了我的问题!
-
@RaymondChen,我同意你的看法!你能否澄清我的另一个疑问,如果
st_c中的缓冲区大小是奇数长度怎么办? -
st_d在st_a之后不需要填充,因为下一个字段是可以字节对齐的unsigned char。在st_a之后的st_c中需要填充,因为下一个字段是st_b,它被标记为需要2 字节对齐。想象一下,如果有一个新结构st_e,它与st_c相同,但具有 4 字节对齐。你不能指望st_d猜测buf1和x1是内联st_c还是内联st_e。 -
那么
st_b将是一个(比如说)9 字节的buf,一个填充字节,然后是一个int。
标签: c++ gcc struct pragma gcc4.4