- •Министерство образования Республики Беларусь
- •1. Что такое программа на языке программирования
- •2. Общее знакомство с языком с
- •3. Структура простой программы на языке с
- •4. Что такое программа на языке с
- •5. Представление информации и типы данных в языке с
- •6. Константы
- •7. Переменные
- •8. Элементарный вВод и вывод информации
- •9. Выражения и операции
- •9.1. Арифметические операции
- •9.2. Операция изменения знака
- •9.3. Операции инкремента и декремента
- •9.4. Операция присваивания
- •9.6. Поразрядные логические операции
- •9.8. Логические операции и операции отношения
- •9.9. Условная операция «? :»
- •9.10. Операция последовательного вычисления
- •9.11. Операция определения требуемой памяти в байтах sizeof
- •9.12. Операция приведения типа (type)
- •10. Операторы управления вычислительным процессом
- •10.1. Операторы ветвления if и else
- •10.2. Оператор switch
- •10.3. Оператор цикла while
- •10.4. Оператор цикла do…while
- •10.5. Оператор цикла for
- •10.6. Бесконечные циклы
- •10.7. Другие управляющие средства языка с
- •10.8. Стандартные математические функции
- •11. Вычисление выражений и побочные эффекты
- •11.1. Преобразования типов при вычислении выражений
- •11.2. Побочные эффекты при вычислении выражений
- •12. Массивы
- •12.1. Описание массива
- •12.2. Инициализация массива
- •12.3. Ввод-вывод массива
- •12.4. Двумерные массивы (массивы массивов)
- •13. Указатели
- •14. Адресная арифметика
- •15. Массивы и указатели
- •15.1. Указатели и одномерные массивы
- •15.2. Указатели и двумерные массивы
- •16. Строки
- •17. Массивы строк
- •18. Функции
- •18.1. Определение функции в языке с
- •18.2. Возвращение значений из функции
- •18.3. Формальные и фактические параметры функции
- •18.4. Вызов функции
- •18.5. Объявление и определение функции: прототип функции
- •19. Передача параметров в функции
- •19.1. Способы передачи параметров в функции
- •19.2. Передача параметров в функции в языке с
- •19.3. Передача указателей в функции
- •20. Классы хранения и видимость переменных
- •20.1. Общие положения
- •20.2. Спецификаторы класса памяти
- •20.3. Область видимости функций
- •20.4. Глобальные переменные
- •20.5. Глобальные статические переменные
- •20.6. Локальные переменные
- •20.7. Статические локальные переменные
- •20.8. Регистровые переменные
- •20.9. Выводы
- •21. Организация памяти программы
- •22. Многофайловая компиляция (проекты)
- •23. Передача в функции массивОв
- •23.1. Передача одномерных массивов в функции
- •23.2. Передача двумерных массивов в функции
- •23.3. Передача в функции символьных строк
- •23.4. Возвращение указателей из функций
- •24. Функции с переменным количеством аргументов
- •24.1. Соглашения о вызовах: модификаторы функций
- •24.2. Объявление списка параметров переменной длины
- •25. Передача параметров в функцию main()
- •26. Указатели на функцию
- •27. Стандартные функцИи языка с
- •27.1. Функции для работы со строками
- •27.2. Функции для проверки символов и преобразования данных
- •27.3. Функция быстрой сортировки – gsort()
- •27.4. Функция двоичного поиска – bsearch()
- •28. Работа с файлами
- •28.1. Основные понятия
- •28.2. Основные функции для работы с файлами
- •28.3. Открытие и закрытие файлов
- •28.4. Ввод/вывод символов
- •28.5. Ввод/вывод строк
- •28.6. Форматированный ввод/вывод
- •28.7. Ввод/вывод блоков данных
- •28.8. Другие средства для работы с файлами
- •28.9. Ввод/вывод низкого уровня (префиксный доступ к файлам)
- •29. Типы, определяемые пользователем: Перечисления, структуры и объединения
- •29.1. Переименование типов – оператор typedef
- •29.2. Перечисления (enum)
- •29.3. Основные сведения о структурах
- •29.4. Структурные переменные в памяти компьютера
- •29.5. Доступ к полям структуры
- •29.6. Массивы структур
- •29.7. Структуры и функции
- •29.8. Объединения (union)
- •30. Динамическая память
- •30.1. Понятие динамического объекта
- •30.2 Создание и уничтожение динамических объектов
- •30.3 Динамическое размещение одномерных массивов и строк
- •30.4 Динамическое размещение двумерных массивов
- •30.5. Функции для работы с блоками памяти
- •31. Динамические структуры данных
- •31.1. Понятие структуры данных
- •31.2. Структуры, ссылающиеся на себя
- •31.3. Связанные списки
- •31.5. Очереди
- •Ниже приводятся примеры функций для очереди (структура элемента очереди совпадает со структурой элемента стека в примере выше):
- •32. Препроцессор языка с
- •32.1 Директива включения файлов
- •32.2. Директива определения макрокоманд (макросов)
- •32.3 Директива условной компиляции
- •32.4 Дополнительные директивы препроцессора
29.7. Структуры и функции
Передавать структуры в функции можно по-разному. Допустим нам надо написать функции для точек и прямоугольников.
struct Point {
int x;
int y;
};
struct Rect {
struct Point pnt1;
struct Point pnt2;
int color;
};
Возникает вопрос: а как передавать функциям информацию о точках и прямоугольниках? Существует по крайней мере три подхода: передавать компоненты по отдельности, передавать всю структуру целиком и передавать указатель на структуру. Каждый подход имеет свои плюсы и минусы.
// формирование точки по компонентам x и y
struct Point makepoint(int x, int y) {
struct Point temp;
temp.x = x;
temp.y = y;
return temp;
}
Никакого конфликта между именами аргументов и именами членов структуры не возникает.
Теперь с помощью функции makepoint() можно выполнять инициализацию любой структуры или формировать структурные аргументы для той или иной функции:
struct Rect screen;
struct Point middle;
screen.pnt1 = makepoint(0, 0);
screen.pnt2 = makepoint(25, 50);
middle = makepoint((screen.pnt1.x + screen.pnt2.x) / 2,
(screen.pnt1.y + screen.pnt2.y) / 2);
// сложение двух точек
struct Point addpoint(struct Point p1, struct Point p2) {
p1.x += p2.x;
p1.y += p2.y;
return p1;
}
Здесь оба аргумента и возвращаемое значение – структуры. В функции мы увеличиваем поля прямо в p1 и не используем для этого временной переменной, т.к. структурные параметры функции передаются по значению точно так же, как и любые другие параметры.
Если функции передается большая структура, то эффективнее передавать указатель на структуру, а не создавать в стеке копию структуру целиком. Также, если функция должна изменять структурную переменную, в нее следует передавать указатель на модифицируемую структуру.
// определение местоположения и цвета прямоугольника
struct Rect makerect(struct Rect *p) {
printf("Введите координаты прямоугольника:\n");
scanf("%d%d%d%d", &p->pnt1.x, &p->pnt1.y,
&p->pnt2.x, &p->pnt2.y);
printf("Введите цвет прямоугольника:\n");
scanf("%d", &p->color);
return *p;
}
struct Rect t1, t2;
t2 = makerect(&t1); // t2 == t1
Без каких либо ограничений можно возвращать из функции и указатель на структуру.
29.8. Объединения (union)
Объединения позволяют хранить разнотипные данные в одной и той же области памяти. По своей сути объединение описывает переменную, которая может иметь любой тип из некоторого множества типов. Другими словами, объединение – это способ по-разному обратиться к одной и той же области памяти.
Определение объединения аналогично определению структуры:
union имя_объединения {
описания_элементов;
};
При объявлении объединения для него описывается набор типов значений, которые могут с ним ассоциироваться. В каждый момент времени объединение интерпретируется как значение только одного типа из набора. Контроль над тем, какого типа значение хранится в данный момент в объединении, возлагается на программиста. Память, которая выделяется для объединения, определяется размером наиболее длинного из его элементов. Все элементы объединения размещаются с одного и того же адреса памяти. Значение текущего элемента объединения теряется, когда другому элементу присваивается значение.
Доступ к полям объединения выполняется или через ‘.’, или через ‘->’, если для доступа используется указатель:
имя_объединения.элемент
или
указатель_на_объединение->элемент
Пример объединения, которое можно интерпретировать как знаковое или беззнаковое целое число:
union number {
int svar;
unsigned int uvar;
};
number x = {100}; // описание переменной типа
number *p; // можно описывать указатели на union
number y[10]; // можно описывать массивы
union { // можно и так описывать переменные,
int svar; // без определения имени union
unsigned int uvar;
} numb;
Инициализировать объединение можно только значением, имеющим тип его первого элемента (например, переменную x можно инициализировать лишь значением типа int).
Значение одного из этих двух заданных типов может быть присвоено переменной x и далее использовано в выражениях, если это правомерно, т.е. если тип взятого из нее значения совпадает с типом последнего присвоенного ей значения. Выполнение этого требования в каждый текущий момент – задача программиста. В том случае, если нечто запомнено как значение одного типа, а извлекается как значение другого типа, результат зависит от реализации.
Объединения могут входить в структуры и массивы, и наоборот. Запись доступа к элементу объединения, находящегося в структуре (как и структуры, находящейся в объединении), такая же, как и для вложенных структур.
Например, в массиве структур
struct {
char *name;
int flags;
int type;
union {
int ival;
float fval;
char *cval;
} u;
} xxx[10];
к ival i-го элемента массива обращаются так: xxx[i].u.ival.
Фактически объединение – это структура, все элементы которой имеют нулевое смещение относительно ее базового адреса, и размер которой позволяет поместиться в ней самому большому ее элементу. Операции, применимые к структурам, годятся и для объединений, т.е. законны присваивание объединения и копирование его как единого целого, получение адреса объединения и доступ к отдельным его элементам.
Пример работы с объединением:
void main() {
union number {
int svar;
unsigned int uvar;
char cvar[2];
};
// аппаратные особенности IBM PC – размещение числовых кодов в памяти,
// начиная с младшего адреса (пары младших разрядов шестнадцатиричного
// числового кода размещаются в байтах памяти с меньшими адресами, т.е.
// при хранении в памяти целых чисел для int байты переставляются,
// для long переставляются сначала по 2 байта, а затем уже байты
// int: 1б 2б => в памяти 2б 1б
// long: 1б 2б 3б 4б => в памяти 4б 3б 2б 1б
// 5010 = 3216 = 0x32
number x = {50}; // {50, 50, “0x32 0x0” (“2”)}
char ss[50];
strcpy(ss, x.cvar); // ss = “2”
x.svar = -100; // {-100, 65436, “0x9C 0xFF”}
unsigned int xx = x.uvar; // xx = 65436
x.svar >>= 1; // {-50, 65486, “0xCE 0xFF”}
// 1111 1111 1001 1100 => 1111 1111 1100 1110 (размножение знака)
x.svar = -100; // {-100, 65436, “0x9C 0xFF”}
x.uvar >>= 1; // {32718, 32718, “0xCE 0x7F”}
// 1111 1111 1001 1100 => 0111 1111 1100 1110
}
Применение объединений очень сильно ограничено (обработка ввода с клавиатуры, обработка кодов нажатых клавиш и т.п.).