根据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)介绍了几个问题:
不适用于_Generic中引入的任何类型的单个位位字段,而不是_Bool。
错误:'_Generic‘类型的选择器’无符号字符:1‘不兼容任何关联
在注释掉发出错误的行后,
s.uval32类型为无符号int,s.uval16类型为无符号短,s.uval8类型为无符号字符,s.ival32类型为int,s.ival16类型为短,s.ival8类型为符号字符,s.bool1类型为_Bool
对我来说,unsigned short、short、unsigned char和signed char在这里完全出乎意料。
我是不是误解了标准?这是GCC的窃听器吗?
看起来使用_Generic即使是定义良好的东西也是不可移植的.
发布于 2019-09-26 08:27:08
如前所述,没有编译器必须提供对奇数位字段类型的支持.如果是这样的话,它可以随意处理它喜欢的类型--这超出了标准的范围。您实际上是在讨论抽象项的类型,按照标准,抽象项称为“存储单元”。
关于这个神奇的抽象“存储单元”的一切都是错误的行为:
C17§6.7.2.1/11:
实现可以分配任何大到足以容纳位字段的可寻址存储单元。如果有足够的空间,在结构中立即跟随另一个位场的位场应被包装成同一单元的相邻位元。如果空间不足,是否将不适合的位字段放入下一个单元或重叠相邻单元是实现定义的。在一个单元内分配位字段的顺序(高阶到低阶或低阶到高阶)是实现定义的。可寻址存储单元的对齐未指定。
只要永远不要在任何地方使用位字段,所有这些问题就会消失。无论如何,从来没有理由使用它们--这是一个100%多余的特性。
发布于 2019-09-26 09:14:32
是的,这里的clang是正确的,gcc是完全错误的。位字段的类型是定义的类型.句号。这方面的标准没有任何含糊不清之处,gcc的“特征”将其作为包含指定位数的特定类型是不符合的。有很长一段时间的讨论始于
https://gcc.gnu.org/ml/gcc/2016-02/msg00255.html
这基本上表明,他们不愿意让步,并改变为一个更用户友好的模式。
如果您真的对此的实际方面感兴趣,您只需使用强制求值的方法之一,例如+或使用逗号运算符。这将失去_Bool和int位字段之间的区别,但仍然可以给出long和int之间的区别。
https://stackoverflow.com/questions/58112287
复制相似问题