简体   繁体   English

GCC 与 C11 标准中的位域类型

[英]Bit-field types in GCC vs C11 standard

According to the C11 standard (mentioned in this answer ), the standard forces the following types to be supported: _Bool , signed int and unsigned int .根据 C11 标准(在此答案中提到),该标准强制支持以下类型: _Boolsigned intunsigned int Other types can be supported but it is up to the implementation.可以支持其他类型,但取决于实现。

I tried to following code to see what are the types of the bit-fields in practice:我尝试按照代码查看实际中位字段的类型:

#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 ) and ICC ( https://godbolt.org/z/yC_U8C ) behaved as expected: Clang ( https://godbolt.org/z/fjVRwI ) 和 ICC ( https://godbolt_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

But GCC ( https://godbolt.org/z/FS89_b ) introduced several issues:但是 GCC ( https://godbolt.org/z/FS89_b ) 引入了几个问题:

  1. A single bit bit-field defined other than _Bool didn't fit any of the types introduced in the _Generic : _Bool以外定义的单个位域不适合_Generic中引入的任何类型:

error: '_Generic' selector of type 'unsigned char:1' is not compatible with any association错误:“unsigned char:1”类型的“_Generic”选择器与任何关联都不兼容

  1. After commenting out the lines which issued errors I got this:在注释掉发出错误的行后,我得到了这个:

     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

    To me, unsigned short , short , unsigned char and signed char are completely unexpected here.对我来说, unsigned shortshortunsigned charsigned char在这里完全出乎意料。

Did I misunderstand the standard?我误解了标准吗? Is this a GCC bug?这是 GCC 错误吗?

Looks like using _Generic even for well defined stuff is not portable...看起来即使对于定义明确的东西也使用_Generic是不可移植的......

As noted, no compiler has to provide support for oddball bit-field types.如前所述,没有编译器必须提供对古怪位字段类型的支持。 If it does, it is free to treat such types as it pleases - this is beyond the scope of the standard.如果是这样,则可以随意处理此类类型 - 这超出了标准的 scope。 You are essentially talking about the type of the abstract item referred to as "storage unit" by the standard.您本质上是在谈论标准称为“存储单元”的抽象项目的类型。

Everything about this magic abstract "storage unit" is poorly-specified behavior:关于这个神奇的抽象“存储单元”的一切都是不明确的行为:

C17 §6.7.2.1/11: C17 §6.7.2.1/11:

An implementation may allocate any addressable storage unit large enough to hold a bitfield.实现可以分配任何大到足以容纳位域的可寻址存储单元。 If enough space remains, a bit-field that immediately follows another bit-field in a structure shall be packed into adjacent bits of the same unit.如果有足够的空间剩余,紧跟在结构中另一个位域之后的位域将被打包到同一单元的相邻位中。 If insufficient space remains, whether a bit-field that does not fit is put into the next unit or overlaps adjacent units is implementation-defined.如果剩余空间不足,则将不适合的位域放入下一个单元还是与相邻单元重叠是实现定义的。 The order of allocation of bit-fields within a unit (high-order to low-order or low-order to high-order) is implementation-defined.单元内位域的分配顺序(高位到低位或低位到高位)是实现定义的。 The alignment of the addressable storage unit is unspecified.可寻址存储单元的 alignment 未指定。

Simply never use bit-fields anywhere and all these problems will go away.永远不要在任何地方使用位域,所有这些问题都会消失。 There is never a reason to use them anyhow - it is a 100% superfluous feature.无论如何都没有理由使用它们 - 这是一个 100% 多余的功能。

Yes, clang is correct here and gcc is plain wrong.是的,clang 在这里是正确的,而 gcc 是完全错误的。 The type of a bit-field is the one that is defined.位域的类型是定义的类型。 Period.时期。 There is no ambiguity in the standard about this, and gcc's "feature" to have them as specific types that includes the number of specified bits is non-conforming.标准中对此没有歧义,并且 gcc 的“功能”将它们作为包含指定位数的特定类型是不符合标准的。 There has been a long discussion that starts at有一个很长的讨论开始于

https://gcc.gnu.org/ml/gcc/2016-02/msg00255.html https://gcc.gnu.org/ml/gcc/2016-02/msg00255.html

which basically shows that they are not willing to concede and change to a more user-friendly mode.这基本上表明他们不愿意让步并转向更人性化的模式。

If you are really interested in practical aspects of this, you could just use one of the methods that force evaluation, such as + or with a comma operator.如果您真的对此的实际方面感兴趣,您可以只使用一种强制评估的方法,例如+或使用逗号运算符。 This would lose the distinction between _Bool and int bit-fields, but still could give you the distinction between long and int .这将失去_Boolint位域之间的区别,但仍然可以为您提供longint之间的区别。

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

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