Объединения

Последнее обновление: 24.08.2023

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

Для определения объединений применяется ключевое слово union и следующий формальный синтаксис:

union имя_объединения
{
	тип элемент1;
	тип элемент2;
	.............
	тип элементN;
};

Фактически объединение определяется точно также, как и структура, только вместо слова struct используется ключевое слово union.

Так, создадим простейшее объединение, которое хранит символ и его числовой код из таблицы ASCII:

union ascii
{
	int digit;
	char letter;
};

Объединение ascii хранит в одном и том же участки памяти объект int (числовой код символа) и объект char (сам символ). Конкретный размер выделенной памяти будет зависеть от системы и реализации, но в общем случае это будет выглядеть примерно следующим образом:

Объединение union в языке программирования Си

В этом случае объединение сode на большинстве платформ будет занимать 4 байта. Длина элементов, как здесь, может быть разной, и в этом случае размер объединения вычисляется по наибольшему элементу.

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

union ascii code;

При определении переменной объединения мы ее можем сразу инициализировать, но стоит учитывать, что инициализировать мы можем только первый элемент объединения. В данном случае это элемент digit типа int, поэтому мы можем передать ему только целое число:

union ascii code = {120};

Для обращения к элементам объединения, как и в случае со структурами, можно использовать операцию "точка":

#include <stdio.h>

union ascii
{
	int digit;
	char letter;
};

int main(void)
{
	union ascii code;
	code.digit = 120;
	printf("%d - %c \n", code.digit, code.letter);	// 120 - x
	printf("%d - %d \n", code.digit, code.letter);	// 120 - 120
	
	code.letter = 87;
	printf("%d - %c \n", code.digit, code.letter);	// 87 - W
	
	return 0;
}

Здесь создается переменная code, которая представляет объединение ascii. Далее его элементу digit (числовой код символа) присваивается число 120:

code.digit = 120;

Стоит отметить, что, так как оба элемента - letter и digit занимают одну и ту же память, то данные фактически одни и те же, только при обращении к code.digit данные интерпретируются как объект int, а при обращении к code.letter - как объект char.

printf("%d - %c \n", code.digit, code.letter);	// 120 - x
printf("%d - %d \n", code.digit, code.letter);	// 120 - 120

И изменение одного из них приведет к изменению другого.

code.letter = 87;
printf("%d - %c \n", code.digit, code.letter);	// 87 - W

Анонимные объединения и установка псевдонима с помощью typedef

Также можно определять анонимные объединения:

#include <stdio.h>

union
{
	int digit;
	char letter;
} code1, code2;		// переменные code1, code2

int main(void)
{
	code1.digit = 122;
	code2.digit = 84;
	printf("%d - %c \n", code1.digit, code1.letter);
	printf("%d - %c \n", code2.digit, code2.letter);
	
	return 0;
}

С помощью оператора typedef можно задать псевдоним для объединения:

typedef union ascii
{
	int digit;
	char letter;
} ascii_code;

Здесь псевдонимом является идентификатор ascii_code, поэтому следующие определения переменных будут аналогичны:

union ascii code = {22};
ascii_code code2 = {22};

Также можно устанавливать псевдоним для анонимных объединений:

typedef union
{
	int digit;
	char letter;
} ascii_code;

Пример с объединениями

Ключевая возможность объединений состоит в том, что они могут применяться для хранения значений разных типов. Выше мы посмотрели, как объединение может хранить данные одновременно типов int и char. Но в подобном случае все несколько просто - int и char накладываются друг на друга - числовой код символа можно выразить и с помощью типа char, и с помощью типа int. Теперь посмотрим более сложный пример:

#include <stdio.h>

typedef enum{
    NODE_STRING,
    NODE_INT
} node_type;

typedef union
{
    int int_value;
    char* str_value;
} node_data;

typedef struct{

    node_type type;
    node_data data;

} node;

void print_node(node n){

    if(n.type == NODE_STRING){
        printf("String: %s\n", n.data.str_value);
    }
    else if(n.type == NODE_INT){
        printf("Int: %d\n", n.data.int_value);
    }
}

int main(void) {
    node n1; 
    n1.type = NODE_INT; 
    n1.data.int_value=22;

    node n2;
    n2.type = NODE_STRING; 
    n2.data.str_value= "Hello World";
    
    print_node(n1);
    print_node(n2);
    
    return 0;
}

Здесь у нас определяется структура node, которая имеет два поля - тип структуры в виде перечисления node_type и непосредственно данные структуры в виде поля node_data. Перечисление node_type может принимать два значения - NODE_STRING и NODE_INT, указывая, что структура будет хранить соответственно или строку, или целое число. А объединение node_data имеет два поля:

typedef union
{
    int int_value;
    char* str_value;
} node_data;

Если структура представляет число, то для обращения к данным используется поле int_value, а если строку - то поле str_value

Для упрощения вывода данных структуры node определяем отдельную функцию print_node():

void print_node(node n){

    if(n.type == NODE_STRING){
        printf("String: %s\n", n.data.str_value);
    }
    else if(n.type == NODE_INT){
        printf("Int: %d\n", n.data.int_value);
    }
}

В зависимости от типа структуры обращаем к полю n.data.str_value или n.data.int_value

В функции main для тестирования создаем пару структур и выводим их данные на консоль. Консольный вывод программы:

Int: 22
String: Hello World

Указатели на объединения

И как и со структурами, можно определять указатели на объединения. Для обращения к элементам объединения по указателю применяется тот же синтаксис, что и в случае со структурами:

(* указатель_на_объединение).имя_элемента
указатель_на_объединение->имя_элемента

Используем указатели на объединения:

#include <stdio.h>

union ascii
{
	int digit;
	char letter;
};

int main(void)
{
	union ascii code = {45};
	union ascii * p_code = &code;
	printf("%d \n", p_code->digit);	// 45
	p_code->digit= 89;
	printf("%d \n", code.digit);		// 89
	
	return 0;
}
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850