简体   繁体   English

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

[英]Size of struct with bitfields different between Linux (gcc) and Windows (mingw32 gcc)

Similar question, but specific to packed structs: Why would the size of a packed structure be different on Linux and Windows when using gcc? 类似的问题,但具体到打包结构: 为什么在使用gcc时Linux和Windows上打包结构的大小会有所不同?


I'm building a shared library for Linux and Windows that needs to deal with well-structured data over a network connection. 我正在为Linux和Windows构建一个共享库,需要通过网络连接处理结构良好的数据。 I'm using gcc 4.8.2 on Linux, and cross-compiling for Windows targets using i686-pc-mingw32-gcc 4.8.1. 我在Linux上使用gcc 4.8.2,并使用i686-pc-mingw32-gcc 4.8.1对Windows目标进行交叉编译。

I've made this little program to demonstrate the issue (note the GCC attributes are commented out, left them in for reference): 我已经制作了这个小程序来演示这个问题(请注意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;
}

I'm compiling for Linux using: gcc -g -Wall test.c -o ./test 我正在使用以下命令编译Linux: gcc -g -Wall test.c -o ./test

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

Very straightforward I thought. 我觉得很直白。 When run on Linux, the output is what I would expect: 在Linux上运行时,输出就是我所期望的:

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

But on Windows: 但在Windows上:

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

So what's the deal with the Windows sizes? 那么Windows尺寸的处理是什么? Why are they different from Linux in this case? 在这种情况下,为什么它们与Linux不同?


I will eventually need to pack these enums and structs, but this issue appears before any packing is done. 我最终需要打包这些枚举和结构,但这个问题出现在任何包装完成之前。 When enabled though, the results are similar: 启用时,结果类似:

Linux: Linux的:

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

Windows: 视窗:

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

The C specification has an informative annex (Annex J) that summarizes unspecified behavior, undefined behavior, and implementation defined behavior. C规范有一个信息性附件(附件J),它总结了未指定的行为,未定义的行为和实现定义的行为。 Here's what it says about bit-fields. 这是关于位域的说法。

J.3 Implementation-defined behavior J.3实现定义的行为

A conforming implementation is required to document its choice of behavior in each of the areas listed in this subclause. 需要符合要求的实施方案来记录本子条款中列出的每个领域的行为选择。 The following are implementation-defined: 以下是实现定义:

J.3.9 Structures, unions, enumerations, and bit-fields J.3.9结构,联合,枚举和位域

  • Whether a "plain" int bit-field is treated as a signed int bit-field or as an unsigned int bit-field (6.7.2, 6.7.2.1). 是否将“plain”int位字段视为signed int位字段或无符号int位字段(6.7.2,6.7.2.1)。

  • Allowable bit-field types other than _Bool, signed int, and unsigned int (6.7.2.1). 除_Bool,signed int和unsigned int(6.7.2.1)以外的允许位字段类型。

  • Whether atomic types are permitted for bit-fields (6.7.2.1). 是否允许原子类型用于位域(6.7.2.1)。
  • Whether a bit-field can straddle a storage-unit boundary (6.7.2.1). 位域是否可以跨越存储单元边界(6.7.2.1)。
  • The order of allocation of bit-fields within a unit (6.7.2.1). 单元内位域分配的顺序(6.7.2.1)。
  • The alignment of non-bit-field members of structures (6.7.2.1). 结构的非位域成员的对齐(6.7.2.1)。 This should present no problem unless binary data written by one implementation is read by another. 除非一个实现写入的二进制数据被另一个实现读取,否则这应该没有问题。
  • The integer type compatible with each enumerated type (6.7.2.2). 与每个枚举类型兼容的整数类型(6.7.2.2)。

You can draw your own conclusions, but I would not use bit-fields in code that's intended to be portable. 您可以得出自己的结论,但我不会在可移植的代码中使用位字段。


It seems that on windows, the compiler starts a new "unit" every time the type changes. 似乎在Windows上,每次类型更改时,编译器都会启动一个新的“单元”。 So in the unpacked case, you have a word_t (2 bytes) followed by a prio_t (4 bytes), a flag_t (4 bytes), and another word_t (2 bytes) for a total of 12 bytes. 因此在解压缩的情况下,你有一个word_t(2个字节),后跟一个prio_t(4个字节),一个flag_t(4个字节),另一个word_t(2个字节),总共12个字节。 When packed it's 2,1,1,2 for a total of 6. If you declared all the fields as uint16_t, you'll probably get the correct size on windows, but you still have the problem of "the order of allocation of bit-fields within a unit" is implementation defined. 打包时它是2,1,1,2,总共6个。如果你将所有字段声明为uint16_t,你可能会在windows上获得正确的大小,但你仍然有“按位分配顺序”的问题- 单元内的字段“是实现定义的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM