Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
DOROGOVA.pdf
Скачиваний:
245
Добавлен:
05.06.2015
Размер:
853.4 Кб
Скачать
a: 2; // 1 и 2 биты b: 3; // 3-5 биты

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

Пример:

 

int a1;

 

struct

 

{ char

h;

int

b;

double

f;

} str;

 

a1 = sizeof (str);

Переменная а1 получит значение, равное 16 (байт), в то же время если сложить длины всех используемых в структуре типов, то длина структуры str будет равна 13.

Данное несоответствие наблюдается потому, что после размещения в памяти первой переменной h длинной 1 байт, добавляется 3 байта для выравнивания адреса переменной b на границу слова. Кроме того, следует отметить, что понятия "слово" и "двойное слово" зависят от разрядности компьютера, поэтому этот результат верен только для 32 разрядных компьютеров.

Как видно из предыдущих примеров, размер структуры зависит не только от образующих её элементов, но и от самых различных факторов, в том числе и от оптимизатора, используемого при компиляции. Поэтому если необходимо точно знать размер структурного объекта, его необходимо явно вычислить с помощью операции sizeof(), которую можно применяться в двух формах:

sizeof ( имя_структуры). sizeof ( имя_структурного_типа)

Для наших примеров одинаковые результаты дадут: sizeof (struct tovar) ;

sizeof (meat) ; sizeof (sklad_1) ;

9.2. Битовые поля

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

Битовые поля применяются там, где необходимо манипулировать отдельными битами памяти. Для чего это нужно? В основном для управления оборудованием. Например, внешним устройством компьютера или автоматической линией на производстве. Чтобы автоматизировать какой-либо процесс необходимо управлять нестандартным оборудованием, всевозможными датчиками, манипуляторами, моторами и так далее. Все эти устройства могут быть подключены к компьютеру через устройства, называемые контроллерами. Отдельные биты или группы битов контроллера служат для связи внешнего устройства с компьютером, записывая и считывая отдельные биты контроллера компьютер управляет внешним устройством. Поэтому неплохо было бы иметь возможность работать с группами битов как с обычными переменными, для этого и предусмотрены битовые поля.

Битовое поле является элементом структуры и обеспечивает доступ к отдельным битам памяти,

при этом запрещается:

объявлять битовые поля вне структур;

организовывать массивы битовых полей;

применять к полям операцию определения адреса.

Определение типа структуры с битовыми полями имеет следующий вид: struct { unsigned имя_поля1: длина_поля1;

unsigned имя_поля2: длина_поля2;

}

Поля могут иметь тип signed int или unsigned int и занимать от 1 до 16 битов. В полях типа signed крайний левый бит является знаковым.

Поля размещаются в машинном слове в направлении от младших битов к старшим. Длина определяет число битов, отведенное полю, задается целым выражением или константой. Поле нулевой длины обозначает выравнивание на границу следующего слова.

Пример: struct prim

{ int unsigned

PDF created with FinePrint pdfFactory Pro trial version http://www.fineprint.com

int

c: 5; // 6-10 биты

int

d: 1; // 11 бит

unsigned

d: 5;//12-16 биты

} i, j;

Данная структура обеспечивает размещение данных в двух байтах (в одном слове). Если бы последнее поле было задано так: unsigned d: 6, то оно размещалось бы не в первом слове, а с 0 по 5 разряд второго слова.

Пример: Описание по применению контроллера к некоему устройству, на основании которой требуется запрограммировать драйвер устройства.

1.CSR (cтатусный регистр) доступен по адресу 0xС75000.

2.Для генерации цикла обмена данными требуется занести номер функции (0..7) в биты 1-4 статусного регистра, а в бит 0 занести единицу.

3.После завершения цикла обмена бит 0 будет очищен контроллером (установлен в нуль).

Задача по программированию внешнего устройства может выглядеть следующим образом: Активизировать функцию контроллера №6, дождаться завершения цикла.

Вариант решения 1. Проделаем это с помощью операций битовой арифметики:

volatile char *csr = (char*)0xС7500; *csr = (6<<1)|1

while ( (*csr & 1) != 0) ;

Первая строка программы создает указатель на статусный регистр. Обратите внимание на то что: во-первых требуется преобразовать целую шестнадцатеричную константу 0xС7500 к типу "указатель

на char" для совмещения типов правой и левой части выражения;

во-вторых необходимо позаботиться о том, чтобы текст программы не претерпевал изменения при трансляции, что достигается использованием модификатора volatile, отменяющего оптимизацию этой части программы.

Вторая строка засылает код 6 в биты 1..4 (путем сдвига константы 6 на один разряд влево) и устанавливает, то есть заносит 1, в бит 0.

Третья строка реализует цикл ожидания, до тех пор, пока контроллер не очистит бит 0.

Такая программа будет работать, но читать её текст сложно, так как назначение отдельных битов никак не отражено в тексте программы. Использование битовых полей позволит избежать этих недостатков.

Вариант 2. Применение битовых полей.

/* Определение структуры битовых полей*/ struct CSR

{ unsigned busy: 1; // Бит, запускающий цикл unsigned func: 4 ; // 4 бита под функцию

int unused: 3; // Три неиспользуемых старших бита

};

volatile struct CSR *mycsr = (struct CSR *)0xС7500;

mycsr->func = 6;

// занести код функции

mycsr->busy = 1;

//запустить цикл

while ( mycsr->busy != 0 ) ;//ожидание конца цикла

При определении структуры можно дать имена битовыми полями, отражающие их назначение, что будет облегчать понимание текста. Как видно из примера, использование битовых полей и краткого комментария дают исчерпывающую информацию о назначении программы.

9.3. Объединения или смеси

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

Объединение - это некоторая переменная, которая может хранить (в разное время!) объекты различного типа и размера.

Общий вид определения объединения: union имя_типа { список_определений } имя_переменной[,имя_переменной];

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

PDF created with FinePrint pdfFactory Pro trial version http://www.fineprint.com

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

Пусть задано следующее определение объединения:

union range

//

размеры переменных:

{ int ir;

//

2

байта

float fr;

//

4

байта

char cr;

//

1

байт

} z;

 

 

 

В данном объединении определены элементы разного типа. Размер переменной z будет равен размеру самого большого из трех приведенных типов, то есть 4 байтам. В один и тот же момент времени z может иметь значение только одной из переменных ir, fr или cr.

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

z.ir - переменная z трактуется как int;

z.fr - переменная z трактуется как float;

z.cr - переменная z трактуется как char.

Элементом объединения может быть массив.

Пример:

//определение союза (объединения) union tu

{ long a; int b[2]; char c[4];

} ;

union tu u1, *au, mu[5]; // объявление трех переменные типа "union tu" u1.a;

u1.c[2];

// установка указателя на массив объединений mu au = mu;

au->b[1]; mu[2].c[3] ;

Здесь определение нового типа данных "union tu" и создание объектов этого типа происходит раздельно. Объявлены следующие объекты: переменная u1, указатель au на объединение и массив

объединений mu[5].

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

u1.a - переменная u1 имеет тип long;

u1.c[2] - переменная u1 имеет тип char;

au->b[1] - использует указатель au для доступа ко второму элементу массива b в первом элементе массива объединений mu;

mu[2].c[3] - дает доступ к четвертому элементу массива с в третьем элементе массива объединений

mu.

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

Пример: Рассмотрим число типа double в виде последовательности байтов. #include <stdio.h>

void main()

{

union DOUBLE_CHAR8

//определение союза DOUBLE_CHAR8

{double d;

 

unsigned char byte[8];

 

};

 

int i;

 

union DOUBLE_CHAR8 v;

// объявление переменной v

v.d = 2.71828;

 

printf("\nчисло=%f\n",v.d);

 

for (i=0; i<8; i++)

 

printf("byte[%d]= %d\n", i, v.byte[i] );

}

Результат на экране дисплея :

Число= 2.718280

PDF created with FinePrint pdfFactory Pro trial version http://www.fineprint.com

byte[0]= 144 byte[1]= 247 byte[2]= 170 byte[3]= 149 byte[4]= 9 byte[5]= 191 byte[6]= 5 byte[7]= 64

При объявлении переменной v (тип переменной union DOUBLE_CHAR8) будет выделена память размером 8 байт, в которую и записывается число 2.71828, а затем в цикле информация выводится в виде цепочки байтов. Союз тоже можно инициализировать при создании, правда, только значением для первого поля, например:

union DOUBLE_CHAR8 u = { 3.14 };

Такой подход вполне естественен, так как память под полями одна и та же, так что инициализатор для второго поля испортил бы нам первое значение.

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

Пример: Экономия памяти при работе с картотекой. union

{char fio[30]; char adres[80]; int vozrast;

int telefon; } inform;

При использовании объекта infor типа union можно обрабатывать только тот элемент, который получил значение, то есть после присвоения значения элементу inform.fio, не имеет смысла обращаться к другим элементам. Разумеется, этот прием можно использовать только для текущей обработки информации, но никак не для её хранения.

9.4. Перечисления

Этот пользовательский тип данных не создает дополнительных возможностей в части программирования, он всего лишь улучшает читаемость программы. Перечисления позволяют создавать синонимы или имена для последовательности целых чисел, по умолчанию нумерация начинается с нуля и идет подряд. Для создания перечисления используется ключевое слово enum.

Пример:

enum BOOLEAN { FALSE,

TRUE

};

Данное перечисление можно заменить последовательностью из двух строк: const int FALSE = 0; const int TRUE = 1;

То есть перечисление, неявно задаёт символические константы целого типа. После такого объявления имена FALSE и TRUE можно использовать в качестве символических констант нуля и единицы, или использовать созданный тип при передаче параметров в функцию "по значению".

Далее в примере показан прием для передачи параметров в функцию, вместо того, чтобы использовать безликие константы 0 и 1, используются имена FALSE и TRUE, отражающие назначение этих констант.

void f (enum BOOLEAN flag)

{if (flag==FALSE) printf ("ЛОЖЬ\n");

else

printf ("ИСТИНА\n");

}

void main() { f (FALSE); f (TRUE);

}

В таком виде программа лучше читается, но, к сожалению, компилятор не проводит проверки правильности использования типа enum BOOLEAN, например, если программист напишет, не имеющее смысла выражение f(25) ошибки не последует!

Пример:

PDF created with FinePrint pdfFactory Pro trial version http://www.fineprint.com

// Ввести новый тип данных seasons.

enum seasons (spring, summer, autumn, winter); enum seasons а, b, с; // определить переменные

Каждая из переменных а, b, c может принимать одно из четырех значений: spring, summer, autumn и winter.

Во всех предыдущих примерах нумерация выполнялась по умолчанию, то есть начиналась с 0, но можно проводить нумерацию элементов в перечислении произвольно.

Пример: Задать имена для кодирования цветовой гаммы при выводе графической информации на экран терминала.

enum COLOR

{ RED = 10, // начать нумерацию с 10

GREEN,

// GREEN == 11

BLUE,

// BLUE == 12

MAGENTA = 16 };

В перечислении enum COLOR нумерация начинается с 10 (RED), а затем идет подряд (GREEN, BLUE), если нужно сделать пропуск в нумерации, следует явно указать значение следующего элемента

(MAGENTA).

Следующий пример создает по нескольку синонимов для одного и того же числа, а также оставляет "пустые места" в последовательности чисел:

enum PIXELCOLOR

{ black,

// black == 0

background = 0, // Тоже 0 (синоним)

red,

// red == 1

green,

// green == 2

blue = 4,

// пропускаем число 3

white=7

// пропускаем 5,6

};

 

PDF created with FinePrint pdfFactory Pro trial version http://www.fineprint.com

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]