
- •13. Функции пользователя и классы памяти.
- •13.1. Сущность и предназначение функций.
- •13.2. Определение и вызов функции.
- •13.3. Прототип функции.
- •13.4. Область видимости.
- •13.5. Классы памяти объектов в языке Cи.
- •13.6. Разбиение программы на модули.
- •14. Структуры и объединения
- •14.1. Понятие структуры
- •14.2. Декларация структурного типа данных
- •14.3. Объявление структурных переменных
- •14.4. Обращение к полям структуры
- •14.5. Операции со структурой как единым целым
- •14.6. Вложенные структуры
- •14.7. Массивы структур
- •14.8. Размещение структурных переменных в памяти
- •14.9. Битовые поля
- •14.10. Объединения
- •15. Генерация псевдослучайных чисел.
- •16. Файлы в языке с
- •16.1. Типы файлов.
- •16.2. Открытие файла
- •16.3. Закрытие файла
- •16.4. Запись - чтение информации
- •А) Посимвольный ввод-вывод
- •Б) Построчный и форматированный ввод-вывод
- •В) Блоковый ввод-вывод
- •Int fflush(file *stream);
- •16.5. Текстовые файлы
- •16.6. Перенаправление стандартного ввода-вывода
- •16.7. Бинарные файлы
- •16.8. Дополнительные полезные функции
- •16.9. Простейший пример создания собственной базы данных
14.8. Размещение структурных переменных в памяти
Элементы структур в общем случае размещаются в памяти последовательно с учетом выравнивания начальных адресов полей.
Выравнивание (align) означает, что компилятор выбирает адреса переменных (в т.ч. полей структуры) так, чтобы они были кратны некоторой величине. Эта величина определяется типом переменной и особенностями адресации данных этого типа на аппаратном уровне (чаще всего она равна 2). Часто выравнивание не обязательно, но при этом скорость обращения к объекту может снижаться. Если выравнивание производится, компилятор может быть вынужден добавить между полями структуры пустые байты, и тогда размер структуры (определяемый с помощью sizeof) будет превышать сумму размеров ее полей.
14.9. Битовые поля
Наряду с "обычными" типами, допустимыми и для "отдельных" переменных, поля структуры могут иметь особый целочисленный тип, допустимый только для них - битовые поля.
Битовые поля содержат заданное количество бит, необязательно кратное байту, но не превосходящее количество бит в типе int (в С++ Builder'е - 32). Они могут быть знаковыми (по умолчанию - signed) либо беззнаковыми (unsigned) , причем слово signed обычно опускается. В знаковых полях старший бит означает знак числа, аналогично всем целочисленным типам (см. выше тему "Кодирование целых чисел").
Объявление знакового битового поля имеет вид:
int a:размер;
а беззнакового битового поля:
unsigned b:размер;
где размер - количество бит в поле.
Пример:
struct T {
int a:5, b:4;
unsigned c:5;
float d;
unsigned e:2, f:5;
};
Выравнивание, рассмотренное выше, выполняется не для отдельных битовых полей, а только для совокупностей подряд идущих битовых полей (именно поэтому битовые поля применяются лишь в составе структур). Так, в приведенном примере битовые поля a, b и c располагаются в памяти подряд, занимая вместе одну двухбайтовую ячейку, как показано на схеме:
№ бит |
15 14 |
13 12 11 10 9 |
8 7 6 5 |
4 3 2 1 0 |
Поля |
|
c |
b |
a |
Как видно из рисунка, поля располагаются в ячейке от младших бит к старшим; при этом в примере последние 2 бита остались неиспользованными.
Аналогично, поля e и f занимают однобайтовую ячейку:
№ бит |
7 |
6 5 4 3 2 |
1 0 |
Поля |
|
f |
e |
Если размер объединенной ячейки превысил бы размер типа int, компилятор может разбить ее на отдельные меньшие ячейки.
Общая схема распределения памяти под структуру типа Т:
№ байт |
0 1 |
2 3 4 5 |
6 |
Поля |
a,b,c |
d |
e,f |
Заметим, что адрес отдельного битового поля получить невозможно.
Битовые поля позволяют:
уменьшить размер памяти, отводимой под переменную. В примере поля a,b,c,e,f в сумме заняли лишь 3 байта;
они упрощают доступ к отдельным битам какой-либо ячейки. Например, если структура с битовыми полями сама является полем объединения (см. ниже), такие поля дают прямой доступ к отдельным битам других полей объединения.
14.10. Объединения
Объединение - поименованная совокупность данных разных типов, размещаемых в одной и той же области памяти, размер которой достаточен для хранения наибольшего элемента.
Объединенный тип данных декларируется подобно структурному типу:
union ID_объединения {
описание полей
};
Пример описания объединенного типа:
union word {
int nom;
char str[20];
};
Пример объявления объектов объединенного типа:
union word p_w, mas_w[100];
Объединения применяют для экономии памяти в случае, когда объединяемые элементы логически существуют в разные моменты времени либо требуется разнотипная интерпретация поля данных.
Например, поток сообщений по каналу связи пусть содержит сообщения трех видов:
struct m1 {
char code;
float data[100]; };
struct m2 {
char code;
int mode; };
struct m3 {
char code, note[80]; };
Элемент code - признак вида сообщения. Удобно описать буфер (место для хранения) сообщений в виде
struct m123 {
char code;
union {
float data[100];
int mode;
char note[80]; };
};
Практически все вышесказанное для структур имеет место и для объединений.
Декларация данных типа union, создание переменных этого типа и обращение к полям объединений производится аналогично структурам.
Пример использования переменных типа union:
. . .
union W {
int a;
float b;
char s[5];
};
void main(void) {
W s;
s.a = 4;
printf(“\n Integer a = %d, Sizeof(s.a) = %d”, s.a, sizeof(s.a));
s.b = 1.5;
printf(“\n Float b = %f, Sizeof(s.b) = %d”, s.b, sizeof(s.b));
strcpy(s.s, “Minsk”);
printf(“\n Char[] a = %s, Sizeof(s.s) = %d”, s.s, sizeof(s.s));
printf(“\n Sizeof(s) = %d”, sizeof(s));
getch();
}
Результат работы программы:
Integer a = 4, Sizeof(s.a) = 2
Float b = 1.500000, Sizeof(s.b) = 4
Char[] a = Minsk, Sizeof(s.s) = 5
Sizeof(s) = 5