包装结构是否会影响子结构?

pax*_*blo 14 c windows visual-studio-2015

我们最近发现了一些代码提交给我们的代码库,其代码如下:

#pragma pack(push,1)
struct xyzzy {
    BITMAPINFOHEADER header;
    char plugh;
    long twisty;
} myVar;
Run Code Online (Sandbox Code Playgroud)

我的问题是:包装是否适用于直接结构,或者它是否BITMAPINFOHEADER也会影响包装.我不能看到后一种情况非常有用,因为它会使结构与您从Windows API调用获得的结构不同,例如.举个例子,让我们假设结构是:

typedef struct {
    char aChar;
    DWORD biSize;
} BITMAPINFOHEADER;
Run Code Online (Sandbox Code Playgroud)

对于Windows的打包而不是默认的8,这种结构将大不相同(无论如何,32位,64位可能是16).

BITMAPINFOHEADER由于几乎可以肯定早些时候宣布它是否受到"保护"的包装?如果它被宣布为外部声明的一部分,那么它是否会受到包装的约束?

Mat*_*lia 11

相关文件:

pack在看到pragma之后的第一个struct,union或者class声明生效.pack对定义没有影响.

header 是成员定义,因此不受影响.

它被宣布为外部声明的一部分,它是否会受到包装的约束?

是的,因为它将是一个struct声明.

另外,正如评论中的Lightness Races Orbit在评论中所说,可以在之前找到更有说服力的措辞:

打包类是将其成员直接放在内存中.

即它没有说明这些成员自己包含的内容,可能是数据和/或填充.(如上所述)包装附加到类型的事实似乎强化了这一点


尽管如此,文档仍然足够模糊,因此最好测试这种解释是否正确; gccVC++都按预期运行.并不是说我特别感到惊讶 - 任何不同的东西都会破坏类型系统中的破坏(将指针指向打包结构的成员实际上会提供指向不同于其类型的指针的指针1).

一般的想法是:一旦你完成定义a struct,它的二进制布局是固定的,并且它的任何实例都将符合它,包括压缩结构的子对象.#pragma pack仅在定义新结构时才考虑当前值,并且在这样做时,成员的二进制布局是固定的黑盒子.


笔记

  1. 说实话,这是一个以x86为中心的观点; 具有更强对齐要求的机器会反对即使指向布局正确但未对齐的结构的指针也不是犹太人:尽管相对于给定指针的字段偏移是正确的,但它们实际上并不是可以按原样使用的指针.

    OTOH,给定一个指向未对齐对象的指针,你总能检测到它是未对齐的并且memcpy它是一个正确对齐的位置,所以它没有像打包对象的假设指针一样糟糕,它的布局实际上是未知的,除非你碰巧知道包装其父母.


Wed*_*shi 9

在充分尊重的情况下,我不确定我是否足够好回答一个578K(现在是633k,令人咋舌)的代表提出的问题.但从我所看到的,它只适用于直接结构.

看看下面的代码:

#include<stdio.h>

struct /*__attribute__((__packed__))*/ struct_Inner {
    char a;
    int b;
    char c;
};

struct __attribute__((__packed__)) struct_Outer {
    char a;
    int b;
    char c;
    struct struct_Inner stInner;
};

int main() 
{
   struct struct_Inner oInner;
   struct struct_Outer oOuter;
   printf("\n%d Bytes", sizeof(oInner));
   printf("\n%d Bytes", sizeof(oOuter));

   return 0;
}
Run Code Online (Sandbox Code Playgroud)

打印,

12 Bytes
18 Bytes
Run Code Online (Sandbox Code Playgroud)

当我打包struct_Inner打印时,

6 Bytes
12 Bytes
Run Code Online (Sandbox Code Playgroud)

此代码是使用GCC-7.2.0编译的.

同样,这不是特定于C标准的任何方式(我只是必须阅读),它更多地与编译器做什么有关.

所以,

BITMAPINFOHEADER是否因为它几乎肯定早些时候被宣布而被"保护"了?

我猜是.这完全取决于BITMAPINFOHEADER宣布的方式.

  • 不要担心,在StackOverflow中提供工具特定的"野外观察"(具有充分的工具名称和版本声明),以充分参与回答过程. (6认同)

Jon*_*ler 6

假设GCC(或Clang模拟GCC),您可以在Structure Layout Pragmas中找到一些相关信息,其中表示存在push一个状态堆栈上的当前打包状态:

为了与Microsoft Windows编译器兼容,GCC支持一组#pragma指令,这些指令可更改随后定义的结构(除零宽度位域除外),联合和类的成员的最大对齐.下面的n值始终需要是2的小幂,并以字节为单位指定新的对齐方式.

  1. #pragma pack(n) 只需设置新的对齐方式.
  2. #pragma pack()将对齐设置为编译开始时生效的对齐(另请参阅命令行选项,-fpack-struct[=n]请参阅代码选项选项).
  3. #pragma pack(push[,n]) 将当前对齐设置推送到内部堆栈,然后可选地设置新对齐.
  4. #pragma pack(pop)将对齐设置恢复为保存在内部堆栈顶部的设置(并删除该堆栈条目).注意,#pragma pack([n])这不会影响这个内部堆栈; 因此可以#pragma pack(push)跟随多个#pragma pack(n)实例并由单个最终确定#pragma pack(pop).

因此,#pragma添加的代码也会影响所有后续的结构定义,直到被反对#pragma pack(pop).我会担心的.

文档没有说明如果#pragma pack(pop)在内部堆栈上没有状态时会发生什么.最有可能的是,它会在编译开始时回退到设置状态.


msc*_*msc 6

根据GCC参考:

在下面的示例中struct my_packed_struct,成员紧密地组合在一起,但其成员的内部布局未打包 - 为此,struct my_unpacked_struct需要打包.

  struct my_unpacked_struct
   {
      char c;
      int i;
   };

  struct my_packed_struct __attribute__ ((__packed__))
    {
       char c;
       int  i;
       struct my_unpacked_struct s;
    };
Run Code Online (Sandbox Code Playgroud)

您只能在枚举,结构或联合的定义上指定此属性,而不能在不定义枚举类型,结构或联合的typedef上指定此属性.

  • TBH这是gcc*及其`__attribute __((__ packed __))`的正确答案*,OP实际上在VC++ 2015上询问了`#pragma pack`.当然我希望它是相同的(因此upvote),但鉴于我们正在讨论特定于编译器的行为和非标准扩展,我希望能够找到解决OP环境细节的答案. (3认同)
  • 您为什么引用GCC参考来回答有关MSVC的问题? (2认同)