在Linux(gcc)和Windows(mingw32 gcc)之间具有不同位域的结构的大小

时间:2015-07-10 20:32:30

标签: c windows gcc struct bit-fields

类似的问题,但特定于打包的结构: Why would the size of a packed structure be different on Linux and Windows when using gcc?

我正在为Linux和Windows构建一个共享库,需要通过网络连接处理结构良好的数据。我在Linux上使用gcc 4.8.2,并使用i686-pc-mingw32-gcc 4.8.1对Windows目标进行交叉编译。

我已经制作了这个小程序来演示这个问题(请注意GCC属性已被注释掉,留待参考):

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

typedef uint16_t word_t;

typedef enum //__attribute__((__packed__))
{
  PRIO_0 = 0,
  PRIO_1,
  PRIO_2,
  PRIO_3,
  PRIO_4,
  PRIO_5,
  PRIO_6,
  PRIO_7,
}
prio_t;

typedef enum //__attribute__((__packed__))
{
  FLAG_A = 0,
  FLAG_B,
}
flag_t;

typedef struct //__attribute__((__packed__))
{
  word_t id     : 8;
  prio_t prio   : 3;
  flag_t flag_1 : 1;
  flag_t flag_2 : 1;
  flag_t flag_3 : 1;
  flag_t flag_4 : 1;
  word_t spare  : 1;
}
recd_t;


int main(int argc, char *argv[])
{
#define NAME_WIDTH 32

  printf("%-*s = %lu\n", NAME_WIDTH, "sizeof(prio_t)", (unsigned long)sizeof(prio_t));
  printf("%-*s = %lu\n", NAME_WIDTH, "sizeof(flag_t)", (unsigned long)sizeof(flag_t));
  printf("%-*s = %lu\n", NAME_WIDTH, "sizeof(recd_t)", (unsigned long)sizeof(recd_t));

  return 0;
}

我使用以下方法编译Linux: gcc -g -Wall test.c -o ./test

Windows: i686-pc-mingw32-gcc -g -Wall test.c -o ./test.exe

我觉得很直白。在Linux上运行时,输出就是我所期望的:

sizeof(prio_t)                   = 4
sizeof(flag_t)                   = 4
sizeof(recd_t)                   = 4

但在Windows上:

sizeof(prio_t)                   = 4
sizeof(flag_t)                   = 4
sizeof(recd_t)                   = 12

那么Windows尺寸的处理是什么?在这种情况下,为什么它们与Linux不同?

我最终需要打包这些枚举和结构,但这个问题出现在任何打包之前。启用后,结果类似:

Linux的:

sizeof(prio_t)                   = 1
sizeof(flag_t)                   = 1
sizeof(recd_t)                   = 2

视窗:

sizeof(prio_t)                   = 1
sizeof(flag_t)                   = 1
sizeof(recd_t)                   = 6

1 个答案:

答案 0 :(得分:3)

C规范有一个信息性附件(附件J),它总结了未指明的行为,未定义的行为和实现定义的行为。这是关于位域的说法。

  

J.3实施定义的行为

     

需要符合要求的实施来记录其选择   本子条款中列出的每个领域的行为。下列   是实现定义的:

     

J.3.9结构,联合,枚举和位域

     
      
  • 是否&#34;普通&#34; int位字段被视为有符号的int位字段或无符号的int位字段(6.7.2,6.7.2.1)。

  •   
  • 除_Bool,signed int和unsigned int(6.7.2.1)以外的允许位字段类型。

  •   
  • 是否允许原子类型用于位域(6.7.2.1)。
  •   
  • 位字段是否可以跨越存储单元边界(6.7.2.1)。
  •   
  • 单位内的位域分配顺序(6.7.2.1)。
  •   
  • 结构的非位域成员的对齐(6.7.2.1)。除非由一个人写入二进制数据,否则这应该没有问题   实施由另一个人阅读。
  •   
  • 与每个枚举类型兼容的整数类型(6.7.2.2)。
  •   

您可以得出自己的结论,但我不会在可移植的代码中使用位字段。

似乎在Windows上,编译器启动了一个新的&#34;单元&#34;每次类型改变。因此在解压缩的情况下,你有一个word_t(2个字节),后跟一个prio_t(4个字节),一个flag_t(4个字节),另一个word_t(2个字节),总共12个字节。打包时它的2,1,1,2共计6个。如果你将所有字段声明为uint16_t,你可能会在windows上获得正确的大小,但你仍然有<的问题em>&#34;单位&#34; 中位域分配的顺序是实现定义的。