
- •Федеральное агентство по образованию
- •Краткая история языка с
- •Особенности и основные понятия языка Си
- •Структура программы
- •Типы данных
- •Классификация типов данных
- •Вещественные
- •Логический
- •Диапазоны представления данных
- •Объявление переменных
- •Константы в языке Си
- •Операции языка Си. Приоритет операций
- •Операция присваивания
- •Арифметические операции
- •Операции отношения
- •Логические операции
- •Поразрядные операции
- •Операцияsizeof
- •Операция выбора по условию
- •Операция запятая
- •Приведение типов Неявное преобразование типов
- •Явное преобразование типов
- •Основные средства ввода-вывода на Си Понятие потока
- •Стандартные потоки
- •Функции потокового ввода-вывода
- •Функции ввода
- •Ввод символов
- •Ввод строк
- •Форматированный ввод
- •Операторы языка Си
- •Оператор if
- •Оператор switch
- •Оператор while
- •Оператор do while
- •Оператор for
- •Оператор break
- •Оператор continue
- •Функции вывода на экран
- •Вывод символов
- •Функция вывода строк puts()
- •Форматированный вывод
- •Указатели в языке Си
- •Адресная арифметика
- •Массивы
- •Функции в языке с
- •Прототипы функций
- •Указатели типа void
- •Функции, возвращающие указатели
- •Встраиваемые функции
- •Рекурсивные функции
- •Функции с переменным числом аргументов
- •Аргументы функции main()
- •Область действия и область видимости
- •Локальные переменные
- •Локальные переменные в функции Main()
- •Глобальные переменные
- •Классы памяти
- •Указатели на указатели
- •Указатели и многомерные массивы
- •Массивы указателей
- •Указатели на функции
- •Операции над строками символов Определение длины строки
- •Копирование строк
- •Поиск в строках
- •Преобразования символов в строках
- •Другие функции для работы со строками
- •Преобразования строк в числа
- •Преобразования чисел в строки
- •Функции анализа символов
- •Распределение памяти
- •Манипулирование блоками памяти
- •Типы, определяемые пользователем
- •Перечислимый тип
- •Структуры
- •Сложные структуры
- •Битовые поля в структурах
- •Объединения
- •Работа с файлами Связь между потоками и файлами
- •Типы дисковых файлов
- •Открытие файла
- •Запись и чтение данных
- •Форматированный ввод-вывод Форматированный вывод
- •Int fprintf (file *fp, char *fmt, ...);
- •Форматированный ввод
- •Символьный ввод
- •Символьный вывод
- •Блочный ввод-вывод
- •Закрытие файла
- •Директивы препроцессора
- •Директива #include
- •Директива #define
- •Директивы условной компиляции
- •Директива #undef
- •Предопределенные макросы
- •69 Лекции по курсу «Языки программирования» Часть III
Сложные структуры
Включение структур в структуры
Проиллюстрируем это следующим примером.
Допустим, нам необходимо работать с прямоугольниками. Прямоугольник можно задать координатами его противоположных (по диагонали) углов.
Структура
для хранения координат одной точки
имеет следующий вид:
struct coor{
int x;
int y;};
Для задания прямоугольника необходимы две такие структуры, объединенные в единое целое. Это можно сделать следующим образом:
struct pr {
struct coord nl;
struct coord vp;
}; /*pr – прямоугольник; nl – нижний левый угол; vp – верхний правый*/
Это дает нам только новый тип. Необходимо еще объявить переменную: struct pr mybox;
Инициализация struct pr mybox = {{1,2},{11,22}}
Напишем программу, которая вычисляла бы площадь прямоугольника.
int a,b; /*длина и ширина прямоугольника*/
long s; /* площадь прямоугольника */
main() {/*Ввод кординат*/
printf(“Введите корд Х левого нижнего угла > ”);
scanf(“%d”, &mybox.nl.x);
printf(“Введите корд Y левого нижнего угла > ”);
scanf(“%d”, &mybox.nl.y);
printf(“Введите корд Х правого верхнего угла > ”);
scanf(“%d”, &mybox.vp.x);
printf(“Введите корд Y правого верхнего угла > ”);
scanf(“%d”, &mybox.vp.y);
/* Вычисление длины и ширины прямоугольника*/
a = mybox.vp.x - mybox.nl.x;
b = mybox.vp.y - mybox.nl.y;
/* Вычисление и вывод площади*/
s = a * b;
printf(“ Площадь = %ld”, s);}
Структуры, содержащие массивы
Можно определять структуры, содержащие массивы в качестве своих элементов (полей). Массивы могут быть любых типов (int, char и т.д.). Например, в следующем фрагменте кода определяется структурный тип data, содержащий целочисленный массив х из четырех элементов и символьный массив у из десяти элементов:
struct data {
int x[4];
char y[10]; }
Затем можно объявить переменную record этого структурного типа: struct data record;.
Инициализация struct data record ={{1,2,3,4},'' 0123456789''};
Организация этой структуры показана на рисунке.
Чтобы обратиться к отдельному элементу массива, являющегося полем структуры, применяется комбинация точки и индекса массива:
record.х[2] = 100;
record.у[1] = 'х' ;
Массивы структур
Чтобы объявить массив структур, надо сначала задать шаблон структуры, а затем объявить массив. Пусть, например, программа предназначена для ведения телефонной книги. Можно определить структуру для хранения номера телефона каждого абонента:
struct entry {
char fname[10];
char lname[12];
char phone[8];
}
Затем
создаем массив структур типаentry:
struct entry list[100];
Этот оператор объявляет массив с именем list из ста элементов. Каждый элемент представляет собой структуру типа entry и распознается по индексу, как и в любом другом массиве. В каждой из структур содержится три поля, причем эти поля — массивы типа char, Вся получившаяся сложная конструкция данных показана схематически на рисунке.
После объявления массива структур можно использовать его для разнообразных задач обработки данных.
Например, для присвоения данных из одного элемента массива другому можно записать следующее: list[l] = list[5];
Этот оператор присваивает всем полям структуры list[l] значения соответствующих полей структуры list[5].
Можно также перемещать данные между отдельными полями структур. Например, следующий оператор копирует строку list[5].phone в строку list[l].phone:
strcpy(list[1].phone, list[5].phone);
При желании можно даже обмениваться данными между отдельными элементами массивов, являющихся полями структур: list[5].phone[l] = list[2].phone[3];
Этот оператор копирует четвертый символ номера телефона из записи list [2] во вторую позицию номера в записи list[5].
Структуры и указатели
Указатели как поля структур
Указатели-поля структур объявляются точно так же, как и любые указатели, не являющиеся членами структур, т.е. с помощью звездочки:
struct data {
int *p_b;
int *p_c;
} a;
В этом примере определяется и создается структура, в которой оба элемента являются указателями на int. Указатели необходимо инициализировать. Это можно сделать, присвоив им подходящие адреса переменных. Если есть две переменные типа int с именами b и c: int b,c;
то можно записать: a.p_b = &b;
a.p_c = &c;
Теперь указатели получили конкретные значения, и для обращения к ним можно использовать операцию ссылки (*). Выражения *a.p_b и b равны и *a.p_с и с тоже равны.
Самым распространенным указателем, используемым в структурах, можно считать указатель на char. То есть речь идет о строках.
В чем разница между использованием массива типа char и указателя того же типа в качестве элементов структуры?
Пример:
struct msg
char p1[30];
char *p2; /* осторожно: не инициализирован! */
} myptrs;
В сущности, оба эти способа предназначены для "помещения" строк в структуры.
Но в первом случае, если определить структуру с массивом типа char, то каждый экземпляр такой структуры будет непосредственно содержать участок памяти нужной длины для хранения этого массива. Кроме того, этот размер никак нельзя превысить, поместив в массив строку большего размера.
Пример:
struct msg
{
char p1[10];
char p2[10];
} myptrs;
…
strcpy(p1, "абвгдежзиклмн"; /* Ошибка! Строка длиннее массива. */
strcpy(p2, "аб"); /* Нет ошибки, но напрасная трата памяти, */
/* поскольку строка короче массива. */
Если же определить структуру, имеющую в своем составе указатель на строку, то это ограничения отпадут. В каждом экземпляре структуры выделяется место лишь для указателя, а сами строки находятся в других местах (где именно — это не ваша забота). Ни напрасной траты памяти, ни ограничения на длину строки не будет, поскольку сами строки не хранятся непосредственно в структуре. Каждый указатель может указывать на строку любой длины, и строка фактически становится частью структуры, хотя и не помещена память в едином блоке с ней.
Внимание: Если не инициализировать указатель, то можно случайно затереть информацию в памяти, предназначенную для чего-то важного. Поэтому, используя указатели вместо массивов, выполняйте их корректную инициализацию. Это можно проделать с помощью динамического выделения памяти или ассоциируя указатели с адресами реальных переменных.
Указатели на структуры и передача структур в функции
Возьмем уже рассмотренный ранее пример:
typedef
struct {
int b;
char c;
} t;
/*Объявление переменных*/
t a1,a2;
t *p; /* типизированный указатель на структуру */
p = &а1; /* инициализация указателя на структуру */
Обращение к полям структуры без использования указателей: a1.b=25;
При использовании указателя обращение к полям структуры на языке Си возможно 2-мя путями:
1-ый путь |
|
2-ой путь |
(*p).b=25; Наличие скобок обязательно, т.к. операция обращения к элементу структуры (.) имеет более высокий приоритет, чем операция ссылки по указателю (*). |
|
p->b=25; (->) – операция косвенного обращения к элементам структуры или операция обращения к структуре по указателю. |
Необходимость использования указателей на структуры вместо имен самих структур связана с передачей структур в функции. Если мы передаем структуру по значению, то все элементы структуры заносятся в стек. Если структура простая и содержит мало элементов, то это не так страшно. Если же структура в качестве своего элемента содержит массив, то стек может переполниться. Кроме того, если структуру большого размера передавать в функцию целиком по значению, то это связано с дополнительными затратами времени и места на перенос копии структуры в стек и из стека при вызове функции и возврата из нее.
При передаче по адресу (ссылке) в стек занесется только адрес структуры. При этом копирования структуры не происходит, появляется возможность изменять содержимое элементов структуры, а также сокращается время передачи структур.
Для того чтобы передать структуру в функцию по адресу надо:
в заголовке функции, куда надо передать структуру должен быть объявлен указатель на тип структуры;
при вызове этой функции в качестве фактического параметра для указателя на структуру надо передавать адрес соответствующей структуры.
Пример:
typedef struct {
int a;
char b;
} str;
str x; /* передаваемая переменная-структура х типа str */
void func1(str y) /* процедура, в которую передается структура */
{ y.a = 1; //обращение к элементу структуры
}
void func2(str *ptr) /* процедура, в которую передается указатель на структуру */
{
ptr -> a = 1; //обращение к элементу структуры
или
(*ptr).a = 1; //обращение к элементу структуры
}
main(){
func1(x); //вызов процедуры с передачей структуры по значению
func2(&x1); //вызов процедуры с передачей структуры по адресу
}
Указатели и массивы структур
typedef
struct {
int a;
char b;
} str;
str vs[25]; // массив структур
str *ps; // указатель на массив структур
/*инициализация указателя*/
ps=vs; или ps=&vs[0];
/* обращение к элементу структуры */
vs[5].a =25;
или
(*(ps+5)).a=25;
или
(ps+5)->a=25;