Я только начинал свой квест с профсоюзами, когда нашел что-то странное
Если я запускаю эту программу
#include <iostream>
using namespace std;
union myun{
public:
int x;
char c;
};
int main()
{
myun y;
//y.x=65;
y.c='B';
cout<<y.x;
}
На выходе получилось какое-то мусорное значение, которое не меняется, если изменить значение y.c.
Затем я сделал это
#include <iostream>
using namespace std;
union myun{
public:
int x;
char c;
};
int main()
{
myun y;
y.x=65;
y.c='B';
cout<<y.x;
}
Выход был, как и ожидалось, равным 66, потому что y.c = ‘B’ заменяет 65 его значением ASCII (66).
Кто-нибудь может объяснить первый случай?
Это на самом деле неопределенное поведение, чтобы читать от члена профсоюза, который был не последним, кому написано.
Вы можете сделать это, если предметы в союзе макет-совместимый (как определено в стандарте), но это не так здесь с int
а также char
(вернее, это мог быть в том случае, если эти два типа имеют одинаковую ширину в битах, но обычно это не так).
Из стандарта C ++ 03 (заменен C ++ 11 сейчас, но все еще актуален):
В объединении самое большее один из членов данных может быть активным в любое время, то есть значение самое большее одного из членов данных может быть сохранено в объединении в любое время.
Я думаю, что вы можете посмотреть в reinterpret_cast
если вы хотите сделать этот вид наложения деятельности.
С точки зрения того, что на самом деле происходит под покровами в первом, шестнадцатеричное значение выходного числа:
-1218142398 (signed) -> 3076824898 (unsigned) -> B7649F42 (hex)
==
^^
||
0x42 is 'B' ----++
должен предоставить подсказку. y.c='B'
только установка сингла байт этой структуры, оставляя остальные три байта (в моем случае) как неопределенные.
Поместив в y.x=65
линия до этой точки, она устанавливает все четыре байтов, с этими тремя запасными, установленными в ноль. Следовательно, они остаются на нуле, когда вы устанавливаете один байт в следующем присваивании.
Ну, вы вроде как объяснили первый случай, когда показали свое понимание второго случая.
Инициализация символьной части изменяет только один байт в типе данных, который обеспечивает int
, Предполагая 32-битное int, это означает, что 3 байта все еще не инициализированы … Отсюда и мусор.
Вот использование памяти вашего союза:
byte
0 1 2 3
+------------
myun::x | X X X X
myun::c | X - - -
Когда вы установите x
Вы устанавливаете целое число, поэтому все остальные байты инициализируются. Когда вы установите c
Вы изменяете только один байт.
y.c='B';
cout<<y.x;
Это имеет неопределенное поведение. В любой момент времени профсоюз содержит только одного из своих членов. Вы не можете попытаться прочитать int
член, если он на самом деле содержит char
член. Поскольку поведение этого не определено, компилятору разрешено делать то, что он хочет с кодом.
Так как sizeof(int) != sizeof(char)
,
Другими словами, целое число и символ занимают разные объемы памяти (в настоящее время в среднем компьютере значение int составляет 4 байта, char — 1 байт). Союз настолько велик, насколько это самый большой член. Таким образом, когда вы устанавливаете символ, вы устанавливаете только 1 байт памяти — остальные 3 байта — просто случайный мусор.
Либо сначала установите самого большого члена союза, либо сделайте что-то вроде:
memset(&y, 0, sizeof(y));
заполнить весь союз с нуля.
В объединении выделенная память равна размеру наибольшего члена, который в вашем случае составляет 2 байта в случае 16-битного компилятора. Все члены используют одно и то же пространство памяти для хранения своих данных, следовательно, практически, только один тип элемента может быть сохранен за один раз.
Когда вы присвоили значение «B» элементу char, он сохранил значение 66 в области памяти размером 1 байт.
Затем вы попытались вывести значение члена int, который, однако, пытался вычислить значение, считывая значения из 2 байтов памяти, следовательно, вы получили значение мусора.
Локальные переменные (точнее, переменные в стеке, т. Е. Имеющие класс хранения «автоматический») типа POD не инициализируются ни к чему, когда они объявляются, поэтому на 3 байта (или 7 байтов в 64-битной системе) не влияет ваше назначение y.c
будет содержать случайный мусор.
Также обратите внимание, что конкретный байт зависит от назначения y.c
зависит от порядкового номера процессора, поэтому этот код будет вести себя по-разному в разных системах, даже если вы инициализируете y.x
перед назначением y.c
,
Переменная y имеет тип объединения, а длина y составляет четыре байта. Например, расположение памяти у выглядит следующим образом:
---------------------------------
| byte1 | byte2 | byte3 | byte4 |
---------------------------------
1) В первой программе предложение y.c='B';
просто установите byte1, но byte2, byte3, byte4 являются случайными значениями в стеке.
2) во второй программе предложение y.x = 65; установите byte1 как 65, byte2, byte3, byte4 равен нулю. Затем предложение y.c='B';
установите byte1 в качестве целочисленного значения ASCII ‘B’, что дает выход 66.