Использует ли объединение вместо четко определенного литья? - программирование
Подтвердить что ты не робот

Использует ли объединение вместо четко определенного литья?

Сегодня утром у меня была дискуссия с коллегой относительно правильности "кодирующего трюка", чтобы обнаружить сущность.

Трюк был:

bool is_big_endian()
{
  union
  {
    int i;
    char c[sizeof(int)];
  } foo;


  foo.i = 1;
  return (foo.c[0] == 1);
}

Мне кажется, что это использование union неверно, поскольку установка одного члена объединения и чтения другого - это не. Но я должен признать, что это просто чувство, и мне не хватает фактических доказательств, чтобы укрепить мою точку зрения.

Правильно ли этот трюк? Кто здесь?

4b9b3361

Ответ 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 (хотя я использовал компиляторы, где это было не так).