使用#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
之后的st_a
中,该多余的字节后面应跟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
(即1个字节st_a
)和41
(是{{1}的缓冲区的第一个字符)之间引入的填充字节}。
在第二行中,st_b
是在缓冲区的最后一个字符0
和4a
中的字符缓冲区后面的整数a
之间引入的填充字节。
第三行打印所有结构的大小。
答案 0 :(得分:0)
您没有正确检测填充。您是否期望填充为零字节?没有理由期望这一点。填充字节中的值可以是任何值。
您在结尾处看到的零是st_b.x1
的4个字节的一部分。它的值是10,它是一个小的endian 2的补码整数,存储为0A 00 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;
}
这将输出:
offset of p1: 0, offset of p2: 2
清楚地表明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