Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Подбельский Фомин_Программирование на языке СИ_...doc
Скачиваний:
356
Добавлен:
10.08.2019
Размер:
53.81 Mб
Скачать

6.5. Объединения и битовые поля Объединения.

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

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

Здесь:

union - служебное слово, вводящее тип данных "объединение" или объединяющий тип данных;

• СС - имя конкретного объединения;

• символьный массив char hh[2] и целая переменная int ii - элементы (компоненты) объединения.

Схема размещения объединения СС в памяти ЭВМ приведена на рис. 6.4.

Определение объединения:

union {char hh[2]; int ii;} cc;

Участок памяти,

выделенный

объединению СС:

Рис. 6.4. Схема размещения объединения в памяти

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

имя_объединения. имя_элемента

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

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

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

записывает значение 15 в объединение, а с помощью конструкций CC.hh[0], CC.hh[l] можно получить отдельные байты внутреннего представления целого числа 15.

Как и данные других типов, объединение - это конкретный объект, которому выделено место в памяти. Размеры участка памяти, выделяемого для объединения, должны быть достаточны для размещения самого большого элемента объединения. В нашем примере элементы int ii и char hh[2] имеют одинаковую длину, но это не обязательно. Основное свойство объединения состоит в том, что все его элементы размещаются от начала одного и того же участка памяти. А размеры участка памяти, отводимого для объединения, определяются размером самого большого из элементов. Например, для объединения

Размеры объекта-объединения uu равны размерам самого большого из элементов, т.е.:

Объединяющий тип. В рассмотренных примерах определены объединения, но явно не обозначены объединяющие типы. Именованный объединяющий тип вводится с помощью определения такого вида:

union имя_объединяющего_типа

{

определения_элементов

};

где union - спецификатор типа;

имя объединяющего_типа - выбираемый программистом идентификатор;

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

Конструкция union имя_объединяющего_типа играет роль имени типа, т.е. с ее помощью можно вводить объединения-объекты.

Как и для структурных типов, с помощью typedef можно вводить обозначения объединяющих типов, не требующие применения union:

typedef union имя объединяющего_тuna

{

определения _элементов

} обозначение объединяющего_тuna ;

Пример:

На основе такого определения типа можно вводить конкретные объединения двумя способами:

Объединения не относятся ни к скалярным данным, ни к данным агрегирующих типов.

Пример. Для демонстрации возможностей, предоставляемых объединениями, рассмотрим функцию bioskey( ) из стандартной библиотеки (прототип функции в заголовочном файле bios.h) компилятора Turbo С:

В MS-DOS принято, что каждое нажатие на любую значимую клавишу клавиатуры ПЭВМ приводит к занесению в буфер клавиатуры двух байтов, младший из которых (по адресу) содержит ASCII-код клавиши, а старший содержит код, называемый скэн-кодом клавиши. Функция bioskey( ) имеет один параметр, определяющий режим ее выполнения. При обращении bioskey(0) функция при пустом буфере ожидает нажатия любой клавиши, т.е. появления в буфере некоторого кода. Если в буфере уже есть коды, то bioskey(0) выбирает из буфера клавиатуры очередной двухбайтовый код и возвращает его в виде целого числа. Обращение bioskey(l) позволяет проверить наличие хотя бы одного кода в буфере. Если буфер не пуст, bioskey(1) возвращает значение очередного кода из буфера, но не удаляет этот код из буфера.

Следующая программа позволяет получать и печатать указанные выше коды, поступающие в буфер клавиатуры ПЭВМ, работающей под управлением операционной системы MS-DOS:

Обратите внимание на спецификацию преобразования , она предназначена для шестнадцатеричного представления пересылаемого значения. Для каждого байта, получаемого из буфера клавиатуры, печатается его десятичное и шестнадцатеричное представления. Выход из программы осуществляется при нажатии клавиш Ctrl+Z. В этом случае в программу поступает двухбайтовый код "44+26". Нажатие любой другой клавиши вызывает чтение очередного кода.

Пример результатов работы с программой:

В данной программе используется объединение с именем ее, в которое с помощью целочисленного элемента cc.ii помещает результат функция bioskey(O). Затем отдельные байты этого целого числа (для доступа к которым используется char hh[2] -второй элемент объединения) печатаются как скэн - и ASCII-коды. Цикл do будет прерван при появлении ASCII-кода 26 и скэн-кода 44 (сочетание клавиш Ctrl и Z), после чего программа заканчивает работу.

В объединения, кроме переменных и массивов, могут входить и структуры. Однако, прежде чем привести содержательный пример, остановимся на еще одном важном понятии - на битовых полях.