根据C11标准(在this answer中提到),该标准强制支持以下类型:_Bool
,signed int
和unsigned int
。可以支持其他类型,但这取决于实现。
我试图按照以下代码来查看实际中位字段的类型:
#include <stdint.h>
#include <assert.h>
#include <stdio.h>
#define ARG_TYPE(arg) _Generic((arg), \
_Bool : "_Bool", \
char : "char", \
signed char : "signed char", \
unsigned char : "unsigned char", \
short : "short", \
unsigned short : "unsigned short", \
int : "int", \
unsigned int : "unsigned int", \
long : "long", \
unsigned long : "unsigned long", \
long long : "long long", \
unsigned long long : "unsigned long long")
int main(void)
{
struct _s
{
unsigned int uval32 : 32;
unsigned int uval16 : 16;
unsigned int uval8 : 8;
unsigned int uval1 : 1;
signed int ival32 : 32;
signed int ival16 : 16;
signed int ival8 : 8;
signed int ival1 : 1;
_Bool bool1 : 1;
} s = {0};
printf("The type of s.uval32 is %s\n", ARG_TYPE(s.uval32));
printf("The type of s.uval16 is %s\n", ARG_TYPE(s.uval16));
printf("The type of s.uval8 is %s\n", ARG_TYPE(s.uval8));
printf("The type of s.uval1 is %s\n", ARG_TYPE(s.uval1));
printf("The type of s.ival32 is %s\n", ARG_TYPE(s.ival32));
printf("The type of s.ival16 is %s\n", ARG_TYPE(s.ival16));
printf("The type of s.ival8 is %s\n", ARG_TYPE(s.ival8));
printf("The type of s.ival1 is %s\n", ARG_TYPE(s.ival1));
printf("The type of s.bool1 is %s\n", ARG_TYPE(s.bool1));
(void)s;
return 0;
}
Clang(https://godbolt.org/z/fjVRwI)和ICC(https://godbolt.org/z/yC_U8C)表现正常:
The type of s.uval32 is unsigned int
The type of s.uval16 is unsigned int
The type of s.uval8 is unsigned int
The type of s.uval1 is unsigned int
The type of s.ival32 is int
The type of s.ival16 is int
The type of s.ival8 is int
The type of s.ival1 is int
The type of s.bool1 is _Bool
但是GCC(https://godbolt.org/z/FS89_b)引入了几个问题:
_Bool
以外定义的单个位字段不适合_Generic
中引入的任何类型:错误:类型为'unsigned char:1'的'_Generic'选择器不兼容与任何协会
在注释掉发出错误的行后,我得到了:
The type of s.uval32 is unsigned int
The type of s.uval16 is unsigned short
The type of s.uval8 is unsigned char
The type of s.ival32 is int
The type of s.ival16 is short
The type of s.ival8 is signed char
The type of s.bool1 is _Bool
对我来说,unsigned short
,short
,unsigned char
和signed char
在这里完全是意外的。
我是否误解了标准?这是GCC错误吗?
即使对于定义明确的内容,似乎也要使用_Generic
都不是可移植的...
如前所述,没有编译器必须提供对奇数位域类型的支持。如果是这样,可以自由地随意处理此类类型-这超出了标准的范围。您实际上是在谈论标准称为“存储单元”的抽象项目的类型。
关于这个魔术抽象“存储单元”的所有事情都没有明确指定的行为:
C17§6.7.2.1/ 11:
一个实现可以分配足够大的可寻址存储单元来容纳位域。如果仍有足够的空间,则一个位域紧跟在一个结构应打包到同一单元的相邻位中。如果空间不足,是将不合适的位字段放入下一个单元还是与相邻单元重叠实现定义。单位内位域的分配顺序(从高到高)低阶或从低阶到高阶)是执行定义的。的对齐未指定可寻址存储单元。
根本不要在任何地方使用位域,所有这些问题都将消失。从来没有理由使用它们-这是100%多余的功能。
是的,这里的clang是正确的,而gcc是完全错误的。位域的类型是已定义的类型。期。标准对此没有任何歧义,并且gcc的“功能”(包括指定位数的特定类型)不符合要求。已经有很长的讨论开始于
https://gcc.gnu.org/ml/gcc/2016-02/msg00255.html
这基本上表明他们不愿意让步,而是改用更人性化的模式。