
- •Введение в системное программирование Основные понятия и определения Программы и программное обеспечение
- •Системное программирование
- •Этапы подготовки программы
- •Системное программирование
- •Лекция 1
- •1. Язык Си: Общая характеристика, историческая справкаи основные достоинства
- •2. Подготовка к выполнению и выполнение программ
- •3. Элементы языка с
- •Лекция 2
- •1. Понятие типа данных. Переменные и константы. Операция присваивания
- •2.Типы данных в языке си. Описание данных в программе
- •3. Константы в языке Си
- •4. Арифметические операции и арифметические выражения
- •5. Операции отношения, логические операции и логические выражения
- •6. Автоматическое преобразрвание типов и операция приведения
- •7. Простейшие операторы языка си. Составной оператор
- •Лекция 3
- •3. Инициализация переменных и массивов
- •4. Управляющие конструкции языка си
- •Лекция 4
- •1. Адреса и указатели
- •2. Отождествление массивов и указателей.Адресная арифметика
- •3. Указатели на массивы. Массивы указателей и многомерные массивы
- •4. Динамическое выделение памяти под массивы
- •5. Инициализация указателей
- •Лекция 5
- •1. Функции в языке си. Формальные и фактические параметры. Механизм передачи параметров. Возвращаемые значения
- •2. Использование указателей в качестве аргументов функций
- •3. Предварительное описание функций
- •4. Аргументы командной строки
- •Лекция 6
- •1. Ввод и вывод в языке си: Общие концепции
- •2. Файлы данных и каталоги. Внутренняя организация и типы файлов
- •3. Стандартные функции для работы с файлами и каталогами
- •4. Внешние устройства как специальные файлы. Организация обмена со стандартными внешними устройствами
- •5. Операции ввода/вывода через порты микропроцессоров intel 8086/80286
- •Лекция 7
- •1. Общая структура программы на языке си. Время существования и видимость переменных. Блоки
- •2. Классы памяти
- •3. Рекурсивные вызовы функций. Реализация рекурсивных алгоритмов
- •4. Препроцессор языка Си
- •5. Модели памяти, поддерживаемые компилятором ibm c/2
- •Лекция 8
- •1. Структуры в языке си: основные понятия
- •2. Массивы структур
- •3. Указатели на структуры
- •4. Вложение структур
- •5. Структуры и функции
- •6. Объединения
- •7. Перечисления
- •8. Определение и использование новых типов данных
- •9. Классы имен
2. Массивы структур
Отдельные структуры с произвольным общим шаблоном, как и обычные переменные любого типа, могут быть объединены в массивы фиксированной длины. С точки зрения общей теории баз данных такая операция соответствует заданию ограниченного вектора, элементами которого являются объекты некоторого абстрактного структурированного типа. Описания массивов структур в программе строятся на той же самой синтаксической основе, что и описания обычных массивов. Так, в следующем примере
struct BOOK { char author[30]; /* Автор книги */
char title[256]; /* Название книги */
int year; /* Год издания */
int pages; /* Количество страниц */
} catalog[10]; /* Массив структур */
имя catalog об'явлено как массив десяти структур с общим шаблоном BOOK. Организация данных, подобная этой, может быть использована, например, при составлении библиографических каталогов. Для обращения к отдельным элементам массива структур его имя всякий раз необходимо модифицировать при помощи квадратных скобок ([]), задающих операцию взятия элемента массива. Конкретный элемент выбранной из массива структуры выделяется в этом случае обычным образом при помощи символа точка (.). Так, обращение вида
catalog[3].title[4]
задает пятый символ массива title элементов типа char в составе четвертого элемента массива структур catalog. Применяя к какому-либо элементу массива операцию & получения адреса
&catalog[7]
можно найти начало размещения в памяти соответствующей структуры. Замечание. Поскольку имя всякого массива является синонимом своего адреса, то упоминание самого по себе имени массива структур тождественно операции получения адреса нулевого элемента этого массива. В частности, для массива структур catalog из предыдущего примера справедливы равенства
catalog == &catalog[0].author[0] == catalog[0].author
3. Указатели на структуры
В предыдущем параграфе, определяя понятие массива структур, мы сделали первый шаг к пониманию того, что тип struct является совершенно полноправным типом данных языка Си. Теперь, расширяя наши представления о структурах, попробуем определить понятие указателя на структуру и придать ему конкретный смысл в программе. Формально указатель на структуру можно описать подобно тому, как мы это делали для указателей на простые типы данных. Общая схема такого описания должна иметь, видимо, один из следующих форматов:
struct <tag> { member-declaration list } *identifier <, ... >;
или
struct tag *identifier <, ... >;
что находится в полном соответствии с синтаксическими правилами составления описаний в языке Си. В частности, возвращаясь в примерам предыдущих параграфов, мы могли бы написать
struct STUDENT { char name[30];
int group; } *studptr;
определяя тем самым указатель studptr на структурный тип STUDENT. При этом комбинация *studptr должна рассматриваться как сама структура и формально можно задать ссылку на ее отдельный элемент, используя операцию точка (.):
(*studptr).group
Заметим, что в этом примере, как и при определении указателей на массивы (см. Лекцию 4, $ 3), круглые скобки являются существенными, поскольку стандартный приоритет операции получения элемента (.) выше приоритета операции косвенной адресации (*). Однако последняя запись может и не иметь конкретного смысла, поскольку создавая указатель на структуру компилятор не выделяет реальную память под хранение ее элементов. Эта ситуация полностью аналогична той, с которой мы столкнулись в Лекции 4, рассматривая эквивалентность массивов и указателей. В то же время, указатели на структуры обеспечивают принципиальную возможность более гибкого манипулирования данными, нежели сами структуры, поскольку используя аппарат указателей память под размещение элементов структуры можно выделять динамически при помощи функций alloca(), malloc() или realloc() (см. Лекцию 4, $ 4). Так, например, воспользовавшись первой из этих функций, мы можем написать
studptr = (struct STUDENT*)alloca(n*sizeof(struct STUDENT));
зарезервировав тем самым блок памяти, достаточный для размещения массива n структур с шаблоном STUDENT. Теперь, используя обозначения последнего примера, попробуем придать конкретный смысл арифметическим операциям над указателями на структуры. Вспоминая, что увеличивая на единицу значение указателя на простой тип данных, мы заставляли его ссылаться на очередной элемент данных соответствующего типа, нетрудно понять, что операции вида
studptr = studptr + 1 или studptr++
смещают указатель на структурный тип STUDENT на начало очередной структуры этого типа. Используя далее общее понятие эквивалентности массивов и указателей, можно проиндексировать указатель на структуру, понимая эту операцию в смысле равенства адресов
studptr + i == &studptr[i]
или в контексте выделения отдельного элемента структуры
(*(studptr+i)).name[k] == studptr[i].name[k]
Для удобства выделения элементов структур, заданных своими указателями, в языке Си дополнительно введена операция следования, знаком которой является комбинация -> символов '-' и '>'. Ее приоритет совпадает с приоритетом обычной операции получения элемента структуры. Используя эту операцию в нашем примере, вместо обозначения
(*studptr).name
определяющего адрес массива name в составе структуры с указателем studptr, следует писать
studptr->name
что семантически совершенно эквивалентно предыдущей записи.