Как присваиваются значения в следующем союзе?

В следующем коде можно ли предсказать значение int (как?), или это просто мусор?

union a
{
    int i;
    char ch[2];
};
a u;
u.ch[0] = 0;
u.ch[1] = 0;
cout<<u.i;
}

person cirronimbo    schedule 10.08.2012    source источник
comment
возможный дубликат Установить все байты int на (unsigned char)0, гарантированно представляющий ноль?   -  person R. Martinho Fernandes    schedule 10.08.2012
comment
Его поведение undefined, но оно все еще может работать.   -  person askmish    schedule 10.08.2012
comment
@R.MartinhoFernandes: Несколько похоже, но, на мой взгляд, совсем не дубликат.   -  person Gorpik    schedule 10.08.2012
comment
@Горпик какая разница?   -  person R. Martinho Fernandes    schedule 10.08.2012
comment
@R.MartinhoFernandes: На этот раз это касается union, а другой вопрос - простого int. Кроме того, char в union не обязательно занимают ту же память, что и int (в большинстве реализаций на самом деле этого не происходит). Как правильно заявляют Лучиан и аскмиш, это УБ, а другое нет.   -  person Gorpik    schedule 10.08.2012
comment
О, ты прав. Я пропустил, что это устанавливает только два байта. Но я не думаю, что это УБ, потому что не вижу разницы между std::memset (reinterpret_cast<char*> (&u.i), (unsigned char)0, 2); и char* p = &u.ch; std::memset (p, (unsigned char)0, 2);.   -  person R. Martinho Fernandes    schedule 10.08.2012
comment
@LuchianGrigore: Это не UB, но меньше.   -  person cirronimbo    schedule 10.08.2012
comment
@R.MartinhoFernandes: Там говорится об установке всех 4 байтов в ноль, что наверняка приведет к нулевому целочисленному значению, я знаю, даже если я сделаю u.ch[] = { 0,0,0,0 }, я получить только ноль. Но проблема в том, что здесь задействовано всего два байта.   -  person cirronimbo    schedule 10.08.2012
comment
Это не то, как вы используете союз. Но GCC, например, явно разрешает этот тип каламбура как расширение. В любом случае, в случае sizeof(int)>2 еще нельзя сказать, какое значение будет иметь u.i.   -  person sellibitze    schedule 10.08.2012
comment
@cirronimbo - Скорее всего, но наверняка это не приведет к нулевому результату int. В языке нет никаких гарантий, что все биты объекта являются частью значения. int может иметь некоторые биты типа, говорящие машине, что это int.   -  person Bo Persson    schedule 10.08.2012
comment
возможный дубликат вопроса об объединении в C   -  person Griwes    schedule 11.08.2012


Ответы (1)


Я бы сказал, что это зависит от размера int и char. union содержит память самой большой переменной. Если int составляет 4 байта, а char[2] представляет 2 байта, int потребляет больше памяти, чем char-массив, поэтому вы не инициализируете полную int-память 0, устанавливая все char-переменные. Это зависит от ваших механизмов инициализации памяти, но в основном значение int будет выглядеть случайным, поскольку дополнительные 2 байта заполнены неопределенными значениями.

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

Если вы уверены, что int является самым большим типом данных, вы можете инициализировать весь union, написав

union a
{
    int i;
    char ch[2];
};

void foo()
{
    a u = { 0 };  // Initializes the first field in the union
    cout << u.i;
}

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

person Excelcius    schedule 10.08.2012
comment
И по совпадению это одно из двух основных применений union. (Другим является простой полиморфизм, который не имеет значения в C++.) - person Stefan Majewsky; 10.08.2012
comment
В C++ такое использование union совершенно бесполезно, так как его можно заменить на reinterpret_cast. Тем не менее, действительно полезно в C. - person Morwenn; 10.08.2012
comment
Результат также зависит от порядка следования байтов процессора. Различные результаты будут наблюдаться, например, когда код скомпилирован на MIPS (где char[0] будет старшим байтом) и процессорах x86 (с прямым порядком байтов, а char[0] будет младшим значащим байтом). - person Maksim Skurydzin; 10.08.2012
comment
Доступ к члену объединения, отличному от последнего сохраненного в, не приводит к неопределенному поведению. Согласно C 1999 6.2.6.1 7, байты элемента, которые не соответствуют байтам элемента, сохраненного в, принимают неуказанные значения. Таким образом, программа должна вести себя так, как будто элемент имеет некоторое значение; что отличается от неопределенного поведения, поскольку последнее допускает любое поведение. (С представлениями ловушек могут быть дополнительные сложности, которые я не буду здесь подробно описывать, и они обычно не применяются к простым целочисленным типам.) - person Eric Postpischil; 10.08.2012
comment
Вы правы, спасибо, это не совсем неопределенное поведение. Я отредактировал свой ответ. Я просто думал об этом как о неопределенном поведении, потому что программа может ожидать, что int будет инициализирован равным 0, в то время как значение может быть любым, на самом деле. - person Excelcius; 10.08.2012