Использует ли объединение вместо четко определенного литья?
Сегодня утром у меня была дискуссия с коллегой относительно правильности "кодирующего трюка", чтобы обнаружить сущность.
Трюк был:
bool is_big_endian()
{
union
{
int i;
char c[sizeof(int)];
} foo;
foo.i = 1;
return (foo.c[0] == 1);
}
Мне кажется, что это использование union
неверно, поскольку установка одного члена объединения и чтения другого - это не. Но я должен признать, что это просто чувство, и мне не хватает фактических доказательств, чтобы укрепить мою точку зрения.
Правильно ли этот трюк? Кто здесь?
Ответы
Ответ 1
Ваш код не переносится. Он может работать на некоторых компиляторах, а может и нет.
Вы правы в том, что поведение undefined при попытке получить доступ к неактивному члену объединения [как и в случае с указанным кодом]
$9,5/1
В объединении не более одного из членов данных могут быть активны в любое время, то есть значение не более одного из элементов данных может быть сохранено в союзе в любое время.
Так что foo.c[0] == 1
неверно, потому что c
в данный момент неактивен. Не стесняйтесь исправить меня, если вы считаете, что я ошибаюсь.
Ответ 2
Не делайте этого, лучше используйте что-то вроде следующего:
#include <arpa/inet.h>
//#include <winsock2.h> // <-- for Windows use this instead
#include <stdint.h>
bool is_big_endian() {
uint32_t i = 1;
return i == htonl(i);
}
Объяснение:
Функция htonl преобразует u_long от хоста к порядку байтов сети TCP/IP (что является широкоугольным).
Литература:
Ответ 3
Вы правы, что этот код не имеет четко определенного поведения. Вот как это сделать:
#include <cstring>
bool is_big_endian()
{
static unsigned const i = 1u;
char c[sizeof(unsigned)] = { };
std::memcpy(c, &i, sizeof(c));
return !c[0];
}
// or, alternatively
bool is_big_endian()
{
static unsigned const i = 1u;
return !*static_cast<char const*>(static_cast<void const*>(&i));
}
Ответ 4
Стандарт C только говорит это (6.7.2.1.12):
Каждый член небитового поля структура или объект объединения выровнены в соответствии с реализацией соответствующий его типу.
Исходя из этого, я бы сказал, что поведение вашего конкретного союза четко определено. Поскольку компилятору не разрешено добавлять прописные байты в начале структуры/объединения, int будет выровнен по направлению к началу объединения, так же как и массив char.
Единственный способ для компилятора реализовать этот конкретный союз таким образом, чтобы он не был переносимым, заключается в использовании типа char размером более 8 бит.
Ответ 5
Функция должна быть названа is_little_endian. Я думаю, вы можете использовать этот трюк. Или также приведение к char.
Ответ 6
В коде есть поведение undefined, хотя некоторые (большинство?) компиляторов будут
определите его, по крайней мере, в ограниченных случаях.
цель стандарта заключается в том, что reinterpret_cast
используется для
это. Однако это намерение плохо выражено, поскольку стандарт
не может действительно определить поведение; нет желания определять его, когда
аппаратное обеспечение не будет поддерживать его (например, из-за проблем с выравниванием). А также
также ясно, что вы не можете просто reinterpret_cast
между двумя
произвольные типы и ожидать, что он будет работать.
С точки зрения качества реализации я ожидал бы как
union
трюк и reinterpret_cast
для работы, если union
или
reinterpret_cast
находится в одном функциональном блоке; union
должен
работайте, пока компилятор может видеть, что конечный тип - это union
(хотя я использовал компиляторы, где это было не так).