Инициализация объединения в C
Я столкнулся с этим объективным вопросом на языке программирования C. Вывод для следующего кода должен быть 0 2
, но я не понимаю, почему.
Пожалуйста, объясните процесс инициализации. Здесь код:
#include <stdio.h>
int main()
{
union a
{
int x;
char y[2];
};
union a z = {512};
printf("\n%d %d", z.y[0], z.y[1]);
return 0;
}
Ответы
Ответ 1
Я предполагаю, что вы используете небольшую систему endian, где sizeof int
is 4 bytes (32 bits)
, а sizeof a char
- это 1 byte (8 bits)
, а единица, в которой целые числа представлены в двух дополнительных формах. A union
имеет размер только самого большого члена, и все члены указывают на эту точную память.
Теперь вы пишете в эту память целочисленное значение 512
.
512 в двоичном формате 1000000000
.
или в 32-битной форме двух дополнений:
00000000 00000000 00000010 00000000
.
Теперь преобразуем это в его маленькое представление конца, и вы получите:
00000000 00000010 00000000 00000000
|______| |______|
| |
y[0] y[1]
Теперь см. выше, что происходит, когда вы обращаетесь к нему с помощью индексов массива char
.
Таким образом, y[0]
есть 00000000
, который равен 0
,
и y[1]
есть 00000010
, который равен 2
.
Ответ 2
Память, выделенная для объединения, представляет собой размер самого большого типа в объединении, который в этом случае равен int
. Скажем, размер int
в вашей системе равен 2 байтам, тогда
512
будет 0x200
.
Репрезентация выглядит следующим образом:
0000 0010 0000 0000
| | |
-------------------
Byte 1 Byte 0
Итак, первый байт 0
, а второй - 2
. (О малогабаритных системах)
char
- один байт на всех системах.
Таким образом, доступ z.y[0]
и z.y[1]
равен байту доступа.
z.y[0] = 0000 0000 = 0
z.y[1] = 0000 0010 = 2
Я просто даю вам, как распределяется память, и значение сохраняется. Вам нужно учитывать приведенные ниже точки, поскольку выход зависит от них.
Точки, которые следует отметить:
- Выход полностью зависит от системы.
- Вопросы endianess и
sizeof(int)
, которые будут различаться в разных системах.
PS: память, занятая обоими членами, одинакова в объединении.
Ответ 3
В стандарте говорится, что
6.2.5 Типы:
Тип объединения описывает перекрывающийся непустой набор объектов-членов, каждый из которых имеет опционально заданное имя и, возможно, отдельный тип.
Компилятор выделяет достаточно места для самого большого из членов, которые накладывают друг на друга в этом пространстве. В вашем случае память выделяется для типа данных int
(предполагая 4 байта). Строка
union a z = {512};
инициализирует первый член объединения z
, т.е. x
становится 512
. В двоичном формате он представлен как 0000 0000 0000 0000 0000 0010 0000 0000
на машине 32.
Представление памяти для этого будет зависеть от архитектуры машины. На 32-битной машине это либо будет (хранить наименее значимый байт в наименьшем адресе - Маленький Endian)
Address Value
0x1000 0000 0000
0x1001 0000 0010
0x1002 0000 0000
0x1003 0000 0000
или как (сохраните самый старший байт в наименьшем адресе - Big Endian)
Address Value
0x1000 0000 0000
0x1001 0000 0000
0x1002 0000 0010
0x1003 0000 0000
z.y[0]
получит доступ к содержимому с добавлением 0x1000
, а z.y[1]
получит доступ к контенту по адресу 0x1001
, и это содержимое будет зависеть от указанного выше представления.
Кажется, что ваша машина поддерживает представление Маленького Эндиана, и поэтому z.y[0] = 0
и z.y[1] = 2
и вывод будет 0 2
.
Но, вы должны заметить, что в сноске 95 раздела 6.5.2.3 указано, что
Если элемент, используемый для чтения содержимого объекта объединения, не совпадает с элементом, используемым последним для хранения значения в объекте, то соответствующая часть представления объекта значения реинтерпретируется как представление объекта в новом типе, как описано в 6.2.6 (процесс, иногда называемый "типом" ). Это может быть представление ловушки.
Ответ 4
Размер объединения определяется максимальным размером, чтобы удерживать один его элемент. Итак, вот размер int.
Предполагая, что это 4 байта /int и 1 байт / char, можно сказать: sizeof union a = 4 bytes
.
Теперь посмотрим, как он фактически хранится в памяти:
Например, экземпляр объединения, a
, хранится в 2000-2003:
-
2000 → последний (четвертый/наименее значимый/правый) байт int x, y [0]
-
2001 → 3-й байт int x, y [1]
-
2002 → 2-й байт int x
-
2003 → 1-й байт int x (наиболее значимый)
Теперь, когда вы скажете z = 512:
так как z = 0x00000200,
-
M [2000] = 0x00
-
M [2001] = 0x02
-
M [2002] = 0x00
-
M [2003] = 0x00
Итак, вы печатаете, y [0] и y [1], он будет печатать данные M [2000] и M [2001], которые равны 0 и 2 в десятичной форме соответственно.
Ответ 5
Для автоматических (нестатических) элементов инициализация идентична присваиванию:
union a z;
z.x = 512;