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

时间:2018-03-06 07:08:10

标签: c windows visual-studio-2015

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

BITMAPINFOHEADER

我的问题是:打包是否仅将 应用于直接结构,或者它是否也会影响typedef struct { char aChar; DWORD biSize; } BITMAPINFOHEADER; 的打包。我不能看到后一种情况非常有用,因为它会使结构与您从Windows API调用获得的结构不同,例如。举个例子,让我们假设结构是:

BITMAPINFOHEADER

对于一个打包而不是Windows的默认八个(32位无论如何,对于64位可能是十六个),该结构会有很大的不同。

from selenium import webdriver from selenium.webdriver.common.by import By class Runfftest(): def test(self): """ Inistitate the ff browser launch the browser :return: """ baseUrl = "https://letskodeit.teachable.com/p/practice" driver = webdriver.Firefox(executable_path="/usr/local/bin/geckodriver") driver.maximize_window() driver.get(baseUrl) driver.implicitly_wait(10) header_by_xpath = driver.find_element_by_xpath("//*[@id='navbar']") links_in_header = header_by_xpath.find_elements(By.TAG_NAME,"a") size_of_links = len(links_in_header) print(size_of_links) for linkinheader in links_in_header: linkinheader.click() ff = Runfftest() ff.test() 是否受到保护,因为几乎可以肯定它早先被宣布?如果它被宣布为外部声明的一部分,那么它是否会受到包装的约束?

5 个答案:

答案 0 :(得分:10)

来自relevant documentation

  在看到编译指示后,

pack会在第一个structunionclass声明生效。 pack对定义没有影响。

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

  

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

是的,因为它是struct声明。

此外,正如评论中的轨道中的轻盈竞赛评论一样,可以在之前找到更有说服力的措辞:

  
    

打包一个类就是将其成员直接放在内存中。

  
     

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

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

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

<强> 备注

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

    OTOH,给定一个指向未对齐对象的指针,你总能检测到它是未对齐的,memcpy指向一个正确对齐的位置,所以它不如指向打包对象的假设指针那么糟糕,它的布局是除非你碰巧知道其父母的包装,否则将会有效。

答案 1 :(得分: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;
}

打印,

12 Bytes
18 Bytes

当我打包它打印的struct_Inner时,

6 Bytes
12 Bytes

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

同样,这不是特定于C标准的任何方式(我只是必须阅读),更多的是与编译器有关的内容。

所以,

  

BITMAPINFOHEADER&#34;受保护&#34;因为几乎可以肯定早些时候宣布它的包装?

我想是的。它完全取决于BITMAPINFOHEADER的声明方式。

答案 2 :(得分:6)

假设GCC(或Clang模拟GCC),您可以在Structure Layout Pragmas找到一些相关信息,其中表示push的存在会保留当前包装状态在一堆状态:< / p>

  

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

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

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

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

答案 3 :(得分:6)

根据GCC reference

  

在以下示例中,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;
    };
     

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

答案 4 :(得分:2)

这并没有直接回答这个问题,但可能会提供一个想法,为什么现有的编译器决定不打包子结构,以及为什么未来的编译器不太可能改变它。

传递影响子结构的包装会以微妙的方式破坏类型系统。

考虑:

//Header A.h
typedef struct {
    char aChar;
    DWORD biSize;
} BITMAPINFOHEADER;


// File A.c
#include <A.h>

void doStuffToHeader(BITMAPINFOHEADER* h)
{
    // compute stuff based on data stored in h
    // ...
}


// File B.c
#include <A.h>

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

void foo()
{
    doStuffToHeader(&myVar.header);
}

我将一个指向打包结构的指针传递给一个不知道打包的函数。函数从结构中读取或写入数据的任何尝试都很容易以可怕的方式破坏。如果编译器认为这是不可接受的,则有两种方法可以解决问题:

  • 将子结构透明地解压缩到函数调用的临时函数中,然后重新打包结果。
  • 在内部将xyzzy中标题字段的类型更改为指示它现在是打包类型且与普通BITMAPINFOHEADER不兼容的内容。

这两个都显然有问题。有了这个推理,即使我想编写一个支持子结构打包的编译器,我也会遇到很多后续问题。我希望我的用户很快就会在这方面开始质疑我的设计决策。