-1

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

我有以下结构:

  1. st_a: 挤满了#pragma pack(1)。大小 = 1 个字节。包含位域。
  2. st_b: 挤满了#pragma pack(2)。大小 = 14 字节。
  3. st_c: 挤满了#pragma pack(2)。大小 = 16 字节。包含st_ast_b
  4. st_d: 挤满了#pragma pack(2)。大小 = 16 字节。(的成员) 的包含st_a和内容st_bst_b

因此,由于st_ais 有 1 个字节打包在 下#pragma pack(1),并且由于它在里面st_c打包在 下#pragma pack(2),因此应该有一个额外的填充字节st_c紧随其后st_a,并且该额外字节后面应该是st_b偶数长度的字符缓冲区的内容(10 )。

但是,当我把st_bout 的内容直接放入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_a41它是缓冲区的第一个字符st_b

在第二行中,0是引入的填充字节,4a它是缓冲区的最后一个字符,是字符缓冲区a后面的整数st_d

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

4

1 回答 1

0

您没有正确检测到填充。您是否期望填充为零字节?没有理由期待这一点。填充字节中的值可以是任何值。您最后看到的零是 4 个字节的一部分st_b.x1。它的值是 10,它作为一个小端 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
于 2019-07-11T14:48:06.850 回答