- •Максимов м.Н.
- •3. Скалярные типы и выражения 51
- •5. Адреса, указатели, массивы, память 96
- •6. Функции, указатели, ссылки 133
- •7 Структуры, объединения и классы 171
- •Введение
- •Модуль 1
- •1.2. Этапы подготовки исполняемой программы
- •1.3. Системы счисления
- •Представление чисел от 0 до 16 в разных системах счисления
- •2.1. Общие сведения о программах, лексемах и алфавите
- •2.2. Идентификаторы и служебные слова
- •2.3. Типы данных
- •2.4. Константы
- •Типы, выбираемые компилятором по умолчанию для целых констант
- •ZzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzТаблица 2.3 Данные вещественного типа
- •2.5. Операции
- •2.6. Разделители
- •3. Скалярные типы и выражения
- •3.1. Определение и описание переменных
- •3.2. Явное и неявное преобразование типа
- •Проектные задания
- •Тесты рубежного контроля
- •Квалиметрическая оценка
- •Список литературы
- •Модуль 2
- •4.1. Последовательно выполняемые операторы
- •4.2. Операторы выбора
- •If( выражение) оператор_1 else оператор_2
- •4.3. Операторы цикла
- •4.4. Операторы передачи управления
- •If (условие) break;
- •4.5. Примеры численного моделирования цепей первого порядка
- •5. Адреса, указатели, массивы, память
- •5.1. Указатели и адреса объектов
- •5.2. Адресная арифметика, типы указателей и операции над ними
- •5.3. Свойства указателя типа void*
- •5.4. Свойства объекта cout
- •5.5. Массивы и указатели
- •5.6. Многомерные массивы, массивы указателей, динамические массивы
- •Проектные задания к модулю
- •Тесты рубежного контроля
- •Квалиметрическая оценка
- •6.2. Функции с переменным количеством параметров
- •6.3. Рекурсивные функции
- •6.4. Подставляемые (инлайн-) функции
- •6.5. Функции и массивы
- •6.6. Указатели на функции
- •Void f3(float) (...) // Определение функции
- •Int* f4(char *){...} // Определение функции
- •Проектные задания
- •Тесты рубежного контроля
- •Квалиметрическая оценка
- •Модуль 4
- •7 Структуры, объединения и классы
- •7.1 Структура как тип и совокупность данных
- •7.3 Объединения разнотипных данных
- •7.4 Деревья
- •7.5 Битовые поля структур и объединений
- •7.6 Компонентные функции структурированных объектов
- •7.7 Расширение действия (перегрузка) стандартных операций
- •7.8 Доступ к компонентам структурированного объекта
- •7.9 Классы и шаблоны
- •Проектные задания
- •Тесты рубежного контроля
- •Квалиметрическая оценка
- •Список литературы
- •Приложение 1
- •Приложение 2 Стандартная библиотека функций языка Си
7.3 Объединения разнотипных данных
Со структурами "в близком родстве" находятся объединения, которые вводятся с помощью служебного слова union. Чтобы пояснить "степень родства" объединений со структурами, рассмотрим приведённый ниже пример.
struct ExamStruct { long L; int K[2]; char C[4]; } STR;
|
union ExamUnion { long L; int K[2]; char C[4]; } UNI;
|
sizeof(ExamStruct) = 12 |
sizeof(ExamUnion) = 4 |
В памяти элементы структуры размещаются следующим образом:
Каждому из элементов L, K[2], C[4] отведено по четыре байта
|
В памяти элементы объединения размещаются следующим образом
L
Под все элементы L, K[2], C[4] отводится четыре байта памяти.
|
Итак, объединение можно рассматривать как структуру, все элементы которой при размещении в памяти имеют нулевое смещение от начала. Тем самым все элементы объединения размещаются в одном и том же участке памяти. Размер участка памяти, выделяемого для объединения, определяется максимальной из длин его элементов. В приведённом выше примере все элементы занимают в памяти четыре байта, поэтому объём памяти, занимаемый объектом UNI равен четырём байтам.
Объединения часто используются для того чтобы обеспечить доступ к одному и тому же фрагменту памяти с помощью разных форматов данных ( разными способами), так записав строчку UNI .C[1] мы получили доступ ко второму байту четырёхбайтовой переменной L и соответственно можем его модифицировать.
Приведём фрагмент кода, в котором объединение используется в качестве поля данных структурированного объекта. Этот же фрагмент является хорошим примером использования директивы препроцессора define, с помощью которой создаётся словарь терминов для абстракций из решаемой нами задачи. Так с помощью первой директивы define вводится синоним STAFF для типа данных struct sStaffType. Этот приём с одной стороны позволяет скрывать некоторые детали реализации, а с другой стороны делает наш код более читабельным и простым для понимания.
#define STAFF struct sStaffType
STAFF { // Учебно-вспомогательный персонал
int iYearsOfService; // Время работы (лет)
float fHourlyWage; // Почасовая оплата
};
# define STUDENT struct sStudentType
STUDENT{
float fGradePtAverage; // Средний рейтинг
int iLevel; // Год обучения
};
# define PROFESSOR struct sProfType
PROFESSOR {
int iDepartmentNumber; // Номер кафедры
float fAnnualSalary; // Годовая зарплата
};
# define NODE_TYPE enum eNodeType
typedef NODE_TYPE {student, professor, staff};
# define TREE struct sTree
TREE {
char sLastName[15]; // Фамилия
char sFirstName[15]; // Имя
int iAge; // Возраст
TREE *Left, *Right; // Указатели на левый и правый листья (ветви)
NODE_TYPE tag; // описатель типа узла - студент или профессор или УВП
union {
STUDENT student;
PROFESSOR professor;
STAFF staff;
} uNodeTag; // Обьединение, содержащее информацию по
}; // студенту или сотруднику университета
Итак, обратим внимание на определение структуры TREE, внутри которой есть поле данных uNodeTag, являющееся объединением структурированных типов данных STUDENT, PROFESSOR, STAFF. Предположим, что каждый объект типа TREE должен содержать запись либо о студенте, либо о профессоре, либо о преподавателе причём фамилия, имя и возраст должны присутствовать во всех трёх типах записей, а остальные поля данных у каждого типа будут свои. В записи о студенте будет хранится информация о среднем рейтинге и годе обучения, в записи о профессоре - номер кафедры и годовая зарплата, в записи о преподавателе – время работы и почасовая оплата. Для того чтобы при извлечении записи из массива или базы данных мы знали, какая информация хранится в поле uNodeTag вводится описатель типа узла tag. В соответствии с его определением в программе мы видим, что если он равен нулю, то это поле содержит информацию о студенте, если единица, то о профессоре, если двойку, то о преподавателе. (Естественно значение поля tag, как и значение всех остальных полей, мы задаём самостоятельно перед тем как поместить запись в массив, или в базу данных, или в линейный список и т.д.) Таким образом объект типа TREE с помощью объединения становится универсальным для хранения данных и о студентах, и о преподавателях , и о профессорах.
Ещё раз хочу обратить внимание на определение поля uNodeTag внутри структуры TREE:
union {
STUDENT student;
PROFESSOR professor;
STAFF staff;
} uNodeTag;
Поскольку объект типа uNodeTag необходим нам только внутри структурированного объекта типа TREE , то нет смысла засорять глобальную область и определять новый тип объединения за пределами TREE. Именно поэтому объединение определено внутри структурированного объекта TREE и не имеет имени типа. Зато сразу видно, что собой представляет объект uNodeTag. Такой приём очень распространён при программировании на Windows.