Безопасно ли обнаруживать endianess с помощью соединения?

Другими словами, согласно стандарту C , этот код безопасен? (Предположим, что uint8_t - один байт)

void detectEndianness(void){
    union {
        uint16_t w;
        uint8_t b;
    } a;
    a.w = 0x00FFU;
    if (a.b == 0xFFU) {
        puts("Little endian.");
    }
    else if (a.b == 0U) {
        puts("Big endian.");
    }
    else {
        puts("Qaru endian.");
    }
}

Что, если я изменю это на это? Обратите внимание на третий случай if, о котором я знаю.

a.w = 1U;
if (a.b == 1U) { puts("Little endian."); }
else if (a.b == 0U) { puts ("Big endian."); }
else if (a.b == 0x80U) { /* Special potential */ }
else { puts("Qaru endian."); }

Ответ 1

Цитата из n1570:

6.5.2.3 Элементы структуры и объединения - p3

Постфиксное выражение, за которым следует. оператор и идентификатор обозначает элемент структуры или объект объединения. Значение таково: именованного члена, и является значением lvalue, если первое выражение является именующий.

6.2.6 Представления типов /1 Общие - p7

Когда значение хранится в члене объекта типа объединения, байты представления объекта, которые не соответствуют этому член, но соответствуют другим членам, принимают неопределенные значения.

Это разрешено. И ваш прецедент можно считать одной из намеченных целей, если примечание 95 принимается во внимание (несмотря на то, что оно только информативное):

Если элемент, используемый для чтения содержимого объекта объединения, не является тот же самый, что и последний элемент, используемый для хранения значения в объекте, Соответствующая часть объектного представления значения равна реинтерпретируется как представление объекта в новом типе, как описано в 6.2.6 (процесс, иногда называемый "пингом типа" ). Это может быть представление ловушки.

Теперь, поскольку семейство типов uintN_t определено, что не имеет битов заполнения

7.20.1.1 Целые числа точной ширины - p2

Имя typedef uintN_t обозначает целочисленный тип без знака с ширина N и без битов заполнения. Таким образом, uint24_t обозначает такой неподписанный целочисленный тип с шириной ровно 24 бита.

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

Ответ 2

Стандарт (доступен в связанном онлайн-проекте) в footnote указывает, что ему разрешен доступ к другому члену того же союза чем ранее записанный член:

95) Если элемент, используемый для чтения содержимого объекта объединения, не является то же, что и последний элемент, используемый для хранения значения в объекте, Соответствующая часть объектного представления значения равна реинтерпретируется как представление объекта в новом типе, как описано в 6.2.6 (процесс, иногда называемый "type punning" ). Это может быть представление ловушки.

Но в сноске также упоминается возможное ловушечное представление, и единственным типом данных, который гарантируется стандартом, который должен быть безопасным относительно представлений о ловушках, является unsigned char. Доступ к представлениям ловушек может быть undefined; и хотя я не думаю, что unit_32 может отображать ловушку на вашей платформе, на самом деле реализация зависит от того, является ли доступ к этому члену UB или нет.

Ответ 3

Нет требования, чтобы порядок бит в байте соответствовал порядку соответствующих бит в большем типе. Соответствующая реализация, которая определяет uint32_t и имеет 8-разрядный unsigned char, может, например, хранить верхние 16 бит uint32_t, используя четыре бита из каждого байта, и хранить нижние 16 бит, используя оставшиеся четыре бита каждого байт. С точки зрения Стандарта, любой из 32! перестановки бит будут одинаково приемлемыми.

Было сказано, что любая реализация, которая не является намеренно тупой и предназначена для работы на обычной платформе, будет использовать один из двух порядков [обработка байтов как групп из 8 последовательных бит в порядке 0123 или 3210] и тот, который не использует одно из указанных выше, и нацелен на любую платформу, которая не является полностью неясной, будет использовать 2301 или 1032. Стандарт не запрещает другие заказы, но неспособность их разместить вряд ли вызовет какие-либо проблемы, кроме при использовании тупо надуманных реализаций.