Объединение (union)

red0;;;Лекция 2.1

Объединения.

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

Описание объединения напоминает описание структуры:

union [имя типа] {описание полей}[список имён];

Список имён может содержать имена переменных, указатели,массивы.

Имя типа указывать необязательно,тогда надо указать элемент(ы) в списке имён? Можно указать и то и другое.

Пример.

union ch // ch - имя типа

{double x;

char s[8];

};

union // имени типа нет

{double x;

char s[8];

}q; //переменная q-объединение

Если введён тип, то можно определять (аналогично структурам):

ch v,w[4]; //переменные, массивы

ch *pch; //указатели

ch &t=&v; //ссылка t

Обращение к элементу объединения:

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

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

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

Ссылка на объединение. имя элемента

Примеры: v.x v.s[i] pch->x *(pch).x t.x

Занести значение в объединение можно присвоив его элементу это значение: q.x=1.57E-2;

Назначение объединения-обеспечить возможность доступа к одному и тому же участку памяти с помощью данных разных типов. Это позволяет, например, задав значение вещественного числа x, посмотреть (и вывести) содержимое его отдельных байтов s[i], и таким образом получить внутреннее представление вещественного x. Для вещественного данного такой доступ к отдельным частям невозможен.

//Внутреннее представление вещественных данных

#include <iostream>

#include <iomanip>

using namespace std;

typedef double tip; // рассматриваемый тип обозначим tip

const int L=sizeof(tip); // размер типа в байтах

union {

tip a;

unsigned char u[L];

}q; // q- включает вещ.данное и массив //байтов размера, равного размеру типа

// здесь необходимо учитывать, что значение a в памяти хранится //в перевёрнутом виде: от младшего байта к старшему, //внутренность байта не переворачивается.

//Байты строки u в памяти хранятся в порядке u[0],u[1],…u[L-1]

//т.е. u[0]наложится на младший байт a,…u[L-1] на старший байт //a, поэтому вывод надо начинать с u[L-1], затем u[L-2],… ,u[0]

void binkod (tip n,char s[])

{

//функция формирует строку s с двоичным представлением //вещественного данного n.

int i,j,m;

i=0; //индекс байта строки u.

q.a=n; //значение n помещается в объединение q

m=(L<<3)-1; //индекс байта строки s.

do

{unsigned char x;

x=q.u[i++]; //запись в x очередного байта из u

for( j=0;j<8;j++) // цикл формирует биты от 0 до7

{s[m--]=(x&1)+'0'; // m-го байта строки s.

x=x>>1;

}

}

while (i<L);

s[L<<3]=0; //нуль символ в конец строки s.

}

//Внутреннее 16-ричное представление целого.

void hexkod (tip n,char s[])

{int i,m;

unsigned char x;

char alf[]="0123456789ABCDEF";//алфавит 16-ой системы счисления

i=0;m=(L<<1)-1;

do

{x=q.u[i++];

s[m--]=alf[x&15]; // младшая 16-ричная цифра i-го байта u

s[m--]=alf[(x>>4)]; // старшая 16-ричная цифра i-го байта u

}

while (i<L);

s[L<<1]='\0'; //нуль символ в конец строки s.

}

int main ()

{double x;

char t[(L<<3)+1];

cout<< "\n double—x= ";

cin>> x;

binkod(x,t); cout<< x<< ": \n"<<t<<endl;

hexkod (x,t); cout<< x<< ": "<<t<<endl;

return 0;

}

Выполнение этой программы

double—-x= 3.5

3.5:

0100000000001100000000000000000000000000000000000000000000000000

3.5: 400C000000000000

Замечание 1. Этот способ доступа к значению типа tip (с union ) может быть применён для значения другого простого типа, например, int. Это более универсальный способ. Для использования этой программы для типа int достаточно изменить одно предложение:

typedef int tip;

В главе «Битовые операции» для получения внутреннего кода данных типа int был рассмотрен другой способ с использованием битовых операций и операций сдвига. Тот способ не приемлем для вещественных данных, так как для них не определены битовые операции и операции сдвига.

Замечание 2. Шаблон функции.

Поскольку алгоритм вывода внутреннего кода данного можно использовать для данных разных типов, возникает желание сделать тип параметром этого алгоритма. В языке С++ есть средство параметризации – шаблоны. С помощью шаблона функции можно определить алгоритм,которому конкретный тип передаётся в виде параметра на этапе компиляции, при этом компилятор генерирует код функции, соответствующий этому типу. Это называется инстанцированием шаблона. Тип параметра для инстанцирования задаётся при вызове функции либо явным образом, либо определяется автоматически по типу аргумента. Код функции при повторном вызове с тем же параметром не создаётся снова.

Простейшая функция-шаблон с параметром tip имеет вид:

template <class tip> заголовок_функции {/*тело функции*/}

Пример шаблона функции с параметром tip для получения строки внутреннего двоичного кода данного этого типа.

#include <iostream>

using namespace std;

template <class tip>

void binkod (tip n,char s[])

{//функция формирует строку s с двоичным представлением //вещественного данного n.

const int L=sizeof(tip); // размер типа в байтах

union {

tip a;

unsigned char u[L];

}q;

int i,j,m;

i=0; //индекс байта строки u.

q.a=n; //значение n помещается в объединение q

m=(L<<3)-1; //индекс байта строки s.

do

{unsigned char x;

x=q.u[i++]; //запись в x очередного байта из u

for( j=0;j<8;j++) // цикл формирует биты от 0 до7

{s[m--]=(x&1)+'0'; // m-го байта строки s.

x=x>>1;

}

}

while (i<L);

s[L<<3]=0; //нуль символ в конец строки s.

}

template <class tip>

void hexkod(tip n,char s[])

{//Внутреннее 16-ричное представление вещ. данного

const int L=sizeof(tip);

union {

tip a;

unsigned char u[L];

}q;

int i,m;

unsigned char x;

char alf[]="0123456789ABCDEF";

q.a=n;

i=0;m=L*2-1;

do

{x=q.u[i++];

s[m--]=alf[x&15];

s[m--]=alf[(x>>4)];

}

while (i<L);

s[L<<1]='\0';

}

int main ()

{const int Q=sizeof(long double);

int n;

double u;

float v;

bool p;

char t[(Q<<3)+1];

cout<< "\n int--n ";

cin>> n;

cout<< "\n double--u ";

cin>> u;

cout<< "\n float--v";

cin>> v;

p=true;

//Вызовы функций

binkod( n,t); //либо binkod<int>(n,t);

cout<< n<< ": "<<t<<endl;

hexkod (n,t); //либо hexkod <int>(n,t);

cout<< n<< ": "<<t<<endl;

binkod( u,t); //либо binkod<double>(u,t);

cout<< u<< ": "<<t<<endl;

hexkod (u,t); //либо hexkod <double>(u,t);

cout<< u<< ": "<<t<<endl;

binkod( v,t); //либо binkod<float>( v,t);

cout<< v<< ": "<<t<<endl;

hexkod (v,t); //либо hexkod<float>(v,t);

cout<< v<< ": "<<t<<endl;

binkod( p,t); //либо binkod<bool>(p,t);

cout<< p<< ": "<<t<<endl;

hexkod (p,t); //либо hexkod <bool>(p,t);

cout<< p<< ": "<<t<<endl;

return 0;

}

Результаты работы программы:

1023: 00000000000000000000001111111111

1023: 000003FF

-3.5: 1100000000001100000000000000000000000000000000000000000000000000

-3.5: C00C000000000000

2.5: 01000000001000000000000000000000

2.5: 40200000

1: 00000001

1: 01

//вывод 16-го кода с использованием манипуляторов

cout.precision(8);

q.a=-3.5;

cout.fill('0');

for(int i=7;i>=0;i--)

cout<<hex<<setw(2)<<int(q.u[i])<<' ';

cout<<endl;

//Вывод минимального и максимального значений

cout<<"min - max"<<endl;

for(int i=0;i<6;i++)

q.u[i]=0;

q.u[6]=16;

q.u[7]=0;

cout<<q.a<<" ";

for(int i=0;i<6;i++)

q.u[i]=255;

q.u[6]=239;

q.u[7]=127;

cout<<q.a<<endl;

Объединение (union)