Что не так с этой функцией C, чтобы найти порядковый номер машины во время выполнения? - PullRequest
8 голосов
/ 20 августа 2009

Это то, что я предложил сегодня на собеседовании.

int is_little_endian(void)
{
    union {
        long l;
        char c;
    } u;

    u.l = 1;

    return u.c == 1;
}

Мой интервьюер настаивал на том, что c и l не обязательно начинаются с одного и того же адреса, и поэтому объединение должно быть изменено на: char c[sizeof(long)], а возвращаемое значение должно быть изменено на u.c[0] == 1.

Правильно ли, что члены профсоюза не могут начинаться по одному и тому же адресу?

Ответы [ 8 ]

8 голосов
/ 20 августа 2009

Я не был уверен насчет членов профсоюза, но ТАК пришел на помощь .

Чек лучше записать как:

int is_bigendian(void) {
    const int i = 1;
    return (*(unsigned char*)&i) == 0;
}

Кстати, в C FAQ показаны оба метода: Как я могу определить, является ли порядок байтов в машине с прямым или младшим порядком байтов?

6 голосов
/ 20 августа 2009

Вы правы в том, что «члены профсоюза могут начинаться с одного адреса». Соответствующей частью стандарта является (6.7.2.1 пункт 13):

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

По сути, начальный адрес объединения гарантированно совпадает с начальным адресом каждого из его членов. Я считаю (все еще ищу ссылку), что long гарантированно будет больше, чем char. Если вы предполагаете это, то ваше решение должно * быть действительным.

* Я все еще немного неуверен из-за некоторых интересных формулировок вокруг представления целых и, в частности, целочисленных типов со знаком. Внимательно ознакомьтесь с пунктами 1 и 2 пункта 6.2.6.2.

3 голосов
/ 20 августа 2009

Хотя ваш код, вероятно, будет работать во многих компиляторах, интервьюер прав: как выровнять поля в объединении или структуре полностью зависит от компилятора, и в этом случае символ может быть помещен либо в «начало», либо в "конец". Код интервьюера не оставляет места для сомнений и гарантированно сработает.

1 голос
/ 20 августа 2009

Стандарт говорит, что смещения для каждого элемента в объединении определяются реализацией.

Когда значение сохраняется в элементе объекта типа объединения, байты объекта представление, которое не соответствует этому члену, но соответствует другим членам принимать неопределенные значения. ISO / IEC 9899: 1999 Представление типов 6.5.6.2, пункт 7 (файл pdf)

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

0 голосов
/ 10 марта 2015

Еще не упоминалось о том, что стандарт явно допускает возможность того, что целочисленные представления могут содержать биты заполнения. Лично я хотел бы, чтобы комитет по стандартам предоставил программе простой и удобный способ указать определенные ожидаемые поведения и потребовал, чтобы любой компилятор либо соблюдал такие спецификации, либо отказывался от компиляции; код, который начинается со спецификации "целые числа не должны иметь битов заполнения", будет иметь право предполагать, что это так.

Как таковое, было бы вполне законно (хотя и нечетно) для реализации хранить 35-битные long значения в виде четырех 9-битных символов в формате с прямым порядком байтов, но использовать младший бит первого байта в качестве бит четности. При такой реализации сохранение 1 в long может привести к тому, что четность всего слова станет нечетной, что вынудит реализацию сохранить 1 в бит четности.

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

Код, использующий union, должен корректно работать на всех архитектурах, которые можно просто описать как "big-endian" или "little-endian", и не использовать биты заполнения. Это было бы бессмысленно на некоторых других архитектурах (и действительно, термины «с прямым порядком байтов» и «с прямым порядком байтов» тоже могли бы быть бессмысленными).

0 голосов
/ 21 октября 2009

поправьте меня, если я ошибаюсь, но локальные переменные не инициализируются в 0;

это не лучше:

union {
    long l;
    char c;
} u={0,};
0 голосов
/ 20 августа 2009

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

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

0 голосов
/ 20 августа 2009

У меня вопрос по этому поводу ...

как

u.c [0] == что-нибудь

действительные данные:

union {
    long l;
    char c;
} u;

Как [0] работает с символом?

Мне кажется, это было бы эквивалентно: (* u.c + 0) == всему, что было бы, ну, в общем, дерьмом, учитывая значение u.c, рассматриваемое как указатель, было бы дерьмом.

(Если, возможно, как мне сейчас кажется, какой-то дерьмовый HTML-код съел амперсанд в исходном вопросе ...)

...