GCC与C11标准中的位域类型

问题描述 投票:0回答:2

根据C11标准(在this answer中提到),该标准强制支持以下类型:_Boolsigned intunsigned 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)引入了几个问题:

  1. _Bool以外定义的单个位字段不适合_Generic中引入的任何类型:

错误:类型为'unsigned char:1'的'_Generic'选择器不兼容与任何协会

  1. 在注释掉发出错误的行后,我得到了:

    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 shortshortunsigned charsigned char在这里完全是意外的。

我是否误解了标准?这是GCC错误吗?

即使对于定义明确的内容,似乎也要使用_Generic都不是可移植的...

c types language-lawyer bit-fields c11
2个回答
1
投票

如前所述,没有编译器必须提供对奇数位域类型的支持。如果是这样,可以自由地随意处理此类类型-这超出了标准的范围。您实际上是在谈论标准称为“存储单元”的抽象项目的类型。

关于这个魔术抽象“存储单元”的所有事情都没有明确指定的行为:

C17§6.7.2.1/ 11:

一个实现可以分配足够大的可寻址存储单元来容纳位域。如果仍有足够的空间,则一个位域紧跟在一个结构应打包到同一单元的相邻位中。如果空间不足,是将不合适的位字段放入下一个单元还是与相邻单元重叠实现定义。单位内位域的分配顺序(从高到高)低阶或从低阶到高阶)是执行定义的。的对齐未指定可寻址存储单元。

根本不要在任何地方使用位域,所有这些问题都将消失。从来没有理由使用它们-这是100%多余的功能。


0
投票

是的,这里的clang是正确的,而gcc是完全错误的。位域的类型是已定义的类型。期。标准对此没有任何歧义,并且gcc的“功能”(包括指定位数的特定类型)不符合要求。已经有很长的讨论开始于

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

这基本上表明他们不愿意让步,而是改用更人性化的模式。

© www.soinside.com 2019 - 2024. All rights reserved.