- •Структура как пользовательский тип и совокупность данных
- •Определение шаблона структуры и структурной переменной
- •Расположение структурных переменных в памяти
- •Инициализация структурных переменных
- •Вложенные структуры
- •Операции над структурными переменными
- •Массив структур
- •Структура в качестве аргумента функции
- •Примеры работы со структурами
- •person_list
- •Объединение как пользовательский тип и совокупность данных
- •Определение объединения и переменной объединяющего типа
- •Использование перечисляемого типа для обращения к полям объединения
- •Битовые поля структур и объединений
- •Определение битовых полей
- •Формирование кода символа с помощью битовых полей объединения
- •Формирование байта и вывод на экран его двоичного представления
Например:
struct PointRecType
{int x, y;
};
Имена полей в одном шаблоне структуры должны быть уникальными. Однако в разных шаблонах можно использовать совпадающие имена полей. Более того, имя шаблона может совпадать с именами полей и переменных. Поскольку имена полей «скрыты» внутри шаблона, они могут дублировать «внешние» переменные и поля в других описаниях структур.
Например:
struct PointRecType
{ int x, y;
};
struct ColorPointRecType
{int x, y; |
|
unsigned color; |
|
}; |
|
struct |
// определение шаблона структуры без имени |
{ PointRecType point; |
|
ColorPointRecType color_point; |
|
} big_struct; |
// определение структурной переменной big_struct |
int x, y; |
//определение простых переменных |
PointRecType point; |
//определение структурной переменной типа PointRecType |
ColorPointRecType color_point ; //определение структурной переменной типа ColorPointRecType
Здесь x, point.x,
color_point.x, big_struct.point.x,
big_struct.color_point.x – разные значения.
Как мы видим из примера, элементом определяемого структурного типа может быть другая структура, тип которой уже определен.
Область видимости шаблона структуры:
•локальный шаблон структуры описан внутри блока { } и видим только из пределов данного блока, в частности из пределов конкретной функции;
•если описание шаблона структуры помещено вне блоков, такой шаблон видим во всех функциях ниже точки описания шаблона до границы файла. Нельзя описать шаблон с реквизитом extern.
Расположение структурных переменных в памяти
Изучая структуры, имеет смысл обращать внимание на их представление в памяти компьютера. Порядок описания полей в определении структурного типа задаёт
порядок их расположения в памяти.
Программирование – лекция 18 (лекции Стрикелевой Л.В.)
5
Рассмотрим структуру: struct {
long L; int i1, i2; char c[4];
}STR1;
Впамяти каждый элемент имеет свое место, и размещаются они последовательно:
|
|
|
|
|
|
f0 f1 f2 |
f3 f4 f5 f6 f7 f8 |
f9 fa |
fb fc fd fe ff |
|
|
|
|
|
|
|
|
||||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
long L |
|
|
int i1 |
int i2 |
|
char c[4] |
|
|
|
|
|
|
|
|
Компилятор выделяет под структурную переменную STR1 число байтов памяти,
достаточное для хранения всех ее полей. Однако выделенное количество байт не всегда равно сумме длин отдельных полей из-за влияния дополнительного фактора внутреннего представления структурных переменных, называемого выравниванием.
При выравнивании на границе байта отдельные поля структурной переменной в памяти располагаются без зазоров, с любого четного или нечетного адреса; размер занимаемой структурной переменной памяти равен сумме размеров полей шаблона структуры.
Выравнивание на границе байта устанавливает директива препроцессора #pragma аргумент n задает выравнивание данных в структурах на n байт (
n=(1,2,4,8,16) ). Отмена директивы: #pragma pack(pop). Директива #pragma pack(show)
выводит в качестве warning значение выравнивания по умолчанию, например: value of
pragma pack(show) == 8.
При выравнивании на границе слова компилятор при размещении структурной переменной в памяти может вставлять между полями пустые байты для того, чтобы соблюдались следующие правила:
•отдельная структурная переменная (элемент массива структурных переменных) начинается на границе слова;
•любое поле, тип которого не совпадает с типом char, будет начинаться на границе слова;
•при необходимости в конце структурной переменной добавляются байты.
Расположение в памяти структурной переменной STR2 при наличии выравнивания на границе слова:
struct {
long L; char c[3]; int i1, i2;
} STR2;
|
|
|
|
|
|
f0 f1 f2 |
f3 f4 f5 |
f6 f7 f8 f9 |
fa fb fc fd fe ff |
|
|
|
|
|
|
|
|
|||||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
long L |
|
char c[3] |
|
|
int i1 |
|
|
int i2 |
|
|
|
|
|
|
|
|
Программирование – лекция 18 (лекции Стрикелевой Л.В.)
6
Сумма размеров элементов структуры (4+3+4+4=15) меньше, чем отведено структуре в целом (16) из -за того, что размер массива char c[3]; не кратен длине слова компьютера, и перед элементом int i1 для выравнивания вставлен пустой байт.
Выделенное число байт возвращает операция sizeof .
Инициализация структурных переменных
Разрешается выполнять инициализацию полей структурной переменной при её определении. Например:
struct Book {char name[20]; //определение шаблона структуры char title[44];
int year; float price;
}; |
|
Book |
// инициализация структурных переменных при их определении |
|
first_book = |
|
{“Mitchel M.”, |
|
“Unesennie vetrom”, |
|
2007, |
|
20000 }, |
|
child_book = |
|
{“Troepolskij G.”, |
|
“Belij Bim Chernoe Ucho”, |
|
2006, |
|
10000}, |
|
dog_book = |
|
{…..}; |
Инициализирующие значения располагаются в порядке объявления полей в структурном типе. Типы инициализирующих значений должны быть совместимы с типами соответствующих полей в определении структурного типа.
Вложенные структуры
Как мы видели, поле структурной переменной может иметь любой тип, в том числе и быть другой структурой. В качестве членов структуры можно объявлять массивы других структур.
Поле, являющееся структурой, называют вложенной структурой. Естественно, что шаблон вкладываемой структуры должен быть уже известен компилятору. Например:
struct UDC |
{char class, subclass; int number;}; |
struct Book |
{ struct UDK udc_class; |
|
char name [20]; |
|
chat title[44]; |
|
int year; |
|
float price; |
} first_book, child_book, dog_book;
Программирование – лекция 18 (лекции Стрикелевой Л.В.)
7
Ссылка на поле вложенной структуры формируется из имени структурной переменной, имени структурного поля и имени вложенной структуры. Перечисленные имена разделяются символом «точка». Например:
first_book.udc_class.class = ‘A’; // (first_book.udc_class).class. dog_book.udc_class.number =681;
Теоретически не существует ограничения на величину уровня вложенности структур.
В отношении полей структурного типа существует только одно существенное ограничение – поле не может иметь тот же тип, что и определяемый структурный тип, однако оно может иметь тип указателя на этот тип.
Например:
struct Book |
{ Book |
my_s; |
//это ошибка !!!!! |
|
char name[20]; |
|
|
|
char title[44]; |
|
|
|
int year; |
|
|
}; |
float price; |
|
|
|
|
//OK!!!!! |
|
struct Book |
{ Book* |
my_s; |
char name[20]; char title[44]; int year;
float price;
};
Пусть имеем вложенные структуры:
struct Distance //длина в километрах и метрах
{ int km, m;
};
struct Pole //размеры прямоугольного поля
{ Distance length; Distance width,
};
Тогда инициализация структурной переменной типа Pole выглядит так:
Pole pole = { {2, 20}, {1, 5}};
Операции над структурными переменными
Поля структурной переменной могут использоваться в выражениях и операторах в контексте их типа, в том числе и операторах ввода-вывода. Так же, как и для обычных операндов, действуют правила преобразования типов при смешивании операндов-полей разных типов. Например:
if (strcmp(first_book.name, “Mitchel M.”) == 0)……. if(first_book.year%4==0)…
first_book.name[0] = lower(first_book.name[0]);
Программирование – лекция 18 (лекции Стрикелевой Л.В.)
8
strcpy (first_book. name, “Mitchel M.”); |
//поле-строка получает значение !!!!! |
strcpy (first_book. title, “Unesennie vetrom”); |
|
cin >> child_book.title; |
//полестрока получает значение !!!!! |
first_book. year = 2007; |
|
cin >> first_book. price; |
|
Непосредственно сравнивать две структурные переменные (даже одного типа) нельзя. Сравнивать можно только члены структур. Например:
struct { int a; int b;
} v1, v2;
if ((v1.a == v2.a) && (v1.b == v2.b)) cout << “переменные равны”;
К структурным переменным, определенным с помощью одного и того же
шаблона, применим оператор присваивания.!!! После его выполнения значения полей структурных переменных будут равны. Например:
first_book = child_book;
first_book = child_book = dog_book; RECT r1, r2 = { 2, 6, 4, 2 };
r1.left = 1; r1.top = 5; r1.right = 3; r1.top = 1; r2=r1;
cout<< r2.left << endl; //1
При присваивании, байты памяти, выделенные правой структурной переменной, копируются в память, выделенную компилятором для структурной переменной этого же типа, стоящей слева. Это так называемое поверхностное копирование.
НО!!! Поверхностное копирование членов-указателей может стать источником ошибок в дальнейшей работе. Если в структуре есть члены-указатели, то после присваивания будет существовать больше одной структурной переменной, члены которой указывают на одну и ту же область памяти.!!!
Определить адрес первого байта поля структурной переменной можно обычной операцией взятия адреса.
Если два структурных типа «закольцованы» (поля одного структурного типа объявляются через другой структурный тип и наоборот):
struct A { B *pb;}; struct B { A *pa;};
то допустимо использовать предварительное неполное определение любого из типов:
struct B; |
// предварительное неполное определение типа B; |
struct A { |
B *pb;}; |
struct B { |
A *pa;}; |
В данном случае предварительное неполное определение структурного типа B допустимо использовать в определении структурного типа A, так как определение указателя pb на структуру типа B не требует сведений о размере структуры типа B.
Программирование – лекция 18 (лекции Стрикелевой Л.В.)
9