Linux (gcc) 和 Windows (mingw32 gcc) 之间位域不同的结构大小
Size of struct with bitfields different between Linux (gcc) and Windows (mingw32 gcc)
类似问题,但特定于打包结构: 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
Windows:
sizeof(prio_t) = 1
sizeof(flag_t) = 1
sizeof(recd_t) = 6
C 规范有一个信息性附件 (Annex J),它总结了未指定的行为、未定义的行为和实现定义的行为。这是它对位域的描述。
J.3 Implementation-defined behavior
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
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).
Allowable bit-field types other than _Bool, signed int, and unsigned int (6.7.2.1).
- Whether atomic types are permitted for bit-fields (6.7.2.1).
- Whether a bit-field can straddle a storage-unit boundary (6.7.2.1).
- The order of allocation of bit-fields within a unit (6.7.2.1).
- The alignment of non-bit-field members of structures (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).
您可以得出自己的结论,但我不会在可移植的代码中使用位域。
似乎在 windows 上,每次类型更改时,编译器都会启动一个新的 "unit"。因此,在未打包的情况下,您有一个 word_t(2 个字节),然后是一个 prio_t(4 个字节)、一个 flag_t(4 个字节)和另一个 word_t( 2 个字节)总共 12 个字节。打包后为 2,1,1,2,总共 6 个。如果您将所有字段声明为 uint16_t,您可能会在 windows 上获得正确的大小,但问题仍然存在"the order of allocation of bit-fields within a unit" 的实现定义。
类似问题,但特定于打包结构: 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
Windows:
sizeof(prio_t) = 1
sizeof(flag_t) = 1
sizeof(recd_t) = 6
C 规范有一个信息性附件 (Annex J),它总结了未指定的行为、未定义的行为和实现定义的行为。这是它对位域的描述。
J.3 Implementation-defined behavior
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
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).
Allowable bit-field types other than _Bool, signed int, and unsigned int (6.7.2.1).
- Whether atomic types are permitted for bit-fields (6.7.2.1).
- Whether a bit-field can straddle a storage-unit boundary (6.7.2.1).
- The order of allocation of bit-fields within a unit (6.7.2.1).
- The alignment of non-bit-field members of structures (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).
您可以得出自己的结论,但我不会在可移植的代码中使用位域。
似乎在 windows 上,每次类型更改时,编译器都会启动一个新的 "unit"。因此,在未打包的情况下,您有一个 word_t(2 个字节),然后是一个 prio_t(4 个字节)、一个 flag_t(4 个字节)和另一个 word_t( 2 个字节)总共 12 个字节。打包后为 2,1,1,2,总共 6 个。如果您将所有字段声明为 uint16_t,您可能会在 windows 上获得正确的大小,但问题仍然存在"the order of allocation of bit-fields within a unit" 的实现定义。