Типы битовых полей в стандарте GCC против C11 - PullRequest
1 голос
/ 26 сентября 2019

В соответствии со стандартом C11 (упомянутым в в этом ответе ), стандарт обязывает поддерживать следующие типы: _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) ввел несколько проблем:

  1. Aоднобитовое битовое поле, определенное, кроме _Bool, не подходит ни для одного из типов, представленных в _Generic:

ошибка: селектор типа _Generic типа unsigned char: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 short, short, unsigned char и signed char совершенно неожиданны здесь.

Я неправильно понял стандарт?Это ошибка GCC?

Похоже, что использование _Generic даже для четко определенных вещей не переносимо ...

Ответы [ 2 ]

3 голосов
/ 26 сентября 2019

Да, clang здесь верный, а gcc просто неверный.Тип битового поля - это тот, который определен.Период.В стандарте нет двусмысленности по этому поводу, и «особенность» gcc иметь их в качестве определенных типов, включающую количество указанных битов, не соответствует.Было долгое обсуждение, которое начинается с

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

, которое в основном показывает, что они не хотят уступать и переходить на более удобный для пользователя режим.

Если вы действительно заинтересованы в практических аспектах этого, вы можете просто использовать один из методов, которые вызывают оценку, например + или с запятой.Это потеряло бы различие между _Bool и int битовыми полями, но все же могло бы дать вам различие между long и int.

3 голосов
/ 26 сентября 2019

Как уже отмечалось, ни один компилятор не должен обеспечивать поддержку типов битовых полей странного поля.Если это так, он может относиться к таким типам, как ему угодно, - это выходит за рамки стандарта.По сути, вы говорите о типе абстрактного элемента, называемого стандартом «единица хранения».

Все в этом магическом абстрактном «модуле хранения» плохо определено:

C17 §6.7.2.1 / 11:

Реализация может выделить любой адресуемыйЕдиница хранения, достаточно большая, чтобы держать битовое поле.Если остается достаточно места, битовое поле, которое следует сразу за другим битовым полем в структуре, должно быть упаковано в смежные биты той же единицы.Если остается недостаточно места, определяется ли битовое поле, которое не умещается, в следующем блоке или перекрывает смежные блоки, определяется реализацией.Порядок распределения битовых полей в блоке (от старшего к младшему или от младшего к старшему) определяется реализацией.Выравнивание адресуемой единицы хранения не определено.

Просто никогда нигде не используйте битовые поля, и все эти проблемы исчезнут.В любом случае, нет причин использовать их - это 100% лишняя особенность.

...