- •Часть 2
- •Содержание
- •1.37.18.5. Несовместимость фактического и формального параметров указателей функции……………………………33
- •1. Основы программирования на языке Си
- •1.30. Цикл, управляемый инструкцией while
- •1.31. Цикл, управляемый инструкцией do while
- •1.32. Цикл с выходом
- •1.33. Вложенные циклы
- •1.34. Перечисление
- •1.35. Функция
- •1.35.1. Назначение функции
- •1.35.2. Определение функции
- •1.35.3. Вызов функции.
- •1.35.4. Прототип функции
- •1.35.5. Способы передача параметров
- •1.35.6. Понятие о встраиваемых (inline) функциях
- •1.36. Организация модулей в языке Си
- •1.36.1. Пример организации модуля
- •1.36.2. Защита от повторного включения заголовочного файла
- •1.37. Массивы и указатели
- •1.37.1. Массивы. Общие сведения
- •1.37.1.1. Классификация массивов
- •1.37.1.2. Определение и инициализация массива
- •1.37.1.3. Операции с массивами
- •1.37.2. Указатели. Общие сведения
- •1.37.2.1. Области применения указателей
- •1.37.2.2. Классификация указателей
- •1.37.2.3. Понятие о нулевом указателе
- •1.37.2.4. Понятие о недействительном указателе
- •1.37.2.5. Определение переменной – указателя
- •1.37.2.6. Операции с указателями
- •1.37.2.6.1. Разыменование указателя. Модель переменной-указателя
- •1.37.2.6.3. Оператор присваивания
- •1.37.2.6.4. Инкремент и декремент указателя
- •1.37.2.6.5. Сложение и вычитание целого числа
- •1.37.2.6.6. Вычитание указателей
- •1.37.2.6.7. Индексирование указателей
- •1.37.2.6.8. Сравнение указателей
- •1.37.2.7. Указатели и динамическая память
- •Функция malloc
- •Функция calloc
- •Функция realloc
- •Функция free
- •1.37.3. Указатели и квалификатор const
- •1.37.4. Указатели и квалификатор restrict
- •1.37.5. Связь между указателями и массивами
- •1.37.6. Указатели – параметры функций
- •1.37.7. Массивы – параметры функций
- •1.37.8. Ошибки при работе с указателями
- •1.37.8.1. Ошибки при объявлении указателей
- •1.37.8.2. Использование неинициализированного указателя
- •1.37.8.3. Присваивание несовместимых указателей
- •1.37.8.4. Разыменование нулевого указателя
- •1.37.18.5. Несовместимость фактического и формального параметров указателей функции
- •1.37.8.6. Возврат из функции указателя на локальную переменную
- •1.37.8.7. Висячий указатель
- •1.37.8.8. Утечка памяти
- •1.37.9. Указатели на функцию
- •1.37.9.1. Выражение указатель на функцию
- •1.37.9.2. Определение указателя переменной на функцию
- •1.37.9.3. Инициализация переменной указатель на функцию
- •1.37.9.4. Операции с указателями на функцию
- •1.38.5.Строковые функции стандарта с11
- •1.38.6. Ввод и вывод строк
- •1.39. Работа со структурами
- •1.39.1. Объявление структур
- •1.39.2. Определение структуры в многомодульной программе
- •1.39.3. Инициализация структур
- •1.39.4. Обращение к полям структуры
- •1.39.5. Операции над структурами
- •1.39.6. Массивы структур
- •1.39.6.1. Объявление массивов структур
- •1.39.6.2. Идентификация элементов массива структур
- •1.40. Работа с внешними устройствами
- •1.40.1. Понятие потока
- •1.40.2. Файлы
- •1.40.2.1. Указатель файла
- •1.40.2.2 Функция fopen
- •1.40.2.9. Построковый ввод-вывод
- •1.40.3.Блоковый ввод-вывод
- •Примеры решенных задач
- •2.4. Итерационные циклы
- •2.5. Вложенные циклы
- •2.6. Работа с функциями
- •2.6.1. Преобразование программы в функцию пользователя
- •2.6.2. Функция как обобщенное решение ряда частных задач
- •2.6.3. Параметры функции или глобальные переменные
- •2.6.4. Указатели в качестве параметров функций
- •2.7. Обработка одномерных массивов
- •2.7.1. Массивы ─ входные и выходные параметры функции
- •2.7.2. Ввод массивов
- •2.7.3. Вывод нескольких массивов в виде таблицы
- •2.7.4. Создание нового массива копированием положительных элементов из исходного массива
- •2.7.5. Значения наибольшего и наименьшего элементов массивов
- •2.8. Обработка двумерных массивов
- •2.8.1. Интерфейс функций, работающих с двумерными массивами.
- •2.8.2. Создание и уничтожение двумерного динамического массива
- •2.8.3. Примеры обработки двумерных массивов
- •2.9. Работа со строками
- •2.9.1. Пользовательские аналоги библиотечных функций
- •2.9.2. Копирование чисел, находящихся в строке, в числовой массив
- •2.9.3. Подсчет количества лексем в строке
- •2.9.4. Массивы строк
- •2.10. Указатели на функцию
- •2.10.1. Использование функции qsort
- •2.10.2. Табулирование произвольной функции одного переменного. Новое решение задачи табулирования
- •2.11. Работа со структурами
- •2.11.1. Структуры, поля которых содержат указатели
- •2.11.2. Работа с массивом структур.
- •2.12. Работа с файлами
- •2.12.1. Работа с текстовыми файлами
- •2.12.2. Работа с двоичными файлами
- •2.12.2.1. Вычисление суммы чисел, содержащихся в двоичном файле.
- •2.12.2.2. Чтение числа из заданной позиции двоичного файла
- •2.12.2.3.Замена всех отрицательных чисел двоичного файла нулями
- •Литература
1.36.2. Защита от повторного включения заголовочного файла
В программе может возникнуть ситуация, когда при компиляции файла реализации заголовочный файл другого модуля оказывается включенным несколько раз. Такое повторное включение может привести к сообщениям об ошибках времени компиляции. Это может иметь место при наличии во включенном заголовочном файле определений типов. Такая ситуация характерна для работы со структурами (смотри раздел 1.38). Для защиты от повторного включения обычно применяются две директивы препроцессора: #ifndef … endif и #define. Для примера организуем такую защиту в заголовочном файле модуля my_math, который рассматривался в 1.35.1.
// Файл my_math.h с защитой от повторного включения #ifndef _MY_MATH_H #define _MY_MATH_H double sqr(double x); // … #endif;
Управление включением заголовочного файла my_math.h осуществляется с помощью своего рода флажка, имя которого _MY_MATH_H. Во время первого включения значение флажка не определено. Директива ifndef включает заголовочный файл, только в том случае, когда значение флажка не определено. После первого включения заголовочного файла директива #define назначает флажку некоторое значение. После этого повторное включение оказывается невозможным. Директива #endif ограничивает область действия #ifndef.
1.37. Массивы и указатели
Между массивами и указателями существует сильная связь. Начнем изложение с обсуждения общих сведений, относящихся к массивам.
1.37.1. Массивы. Общие сведения
Массивом называется упорядоченная совокупность однотипных величин, объединенных одним именем. Упорядоченность хранящихся в массиве данных достигается путем использования системы индексов. Количество индексов определяет размерность массива. Для хранения элементов массива выделяется непрерывная область памяти.
Различают одномерные массивы и многомерные массивы. При работе с элементами одномерных массивов используется один индекс, а при работе с многомерными массивами – два и более индексов. Минимальное значение индекса в языке Си принято равным нулю.
Назначение массивов состоит в хранении некоторой совокупности данных для их последующего использования.
1.37.1.1. Классификация массивов
В языке Си имеются три разновидности массивов:
• Массивы с фиксированными размерами (МФР).
• Массивы с переменными размерами (МПР).
• Динамические массивы.
Память для массивов с фиксированными и переменными размерами выделяется с помощью их определения. Выделение и освобождение памяти для динамических массивов выполняется с использованием библиотечных функций.
1.37.1.2. Определение и инициализация массива
Определение массива позволяет компилятору создать массив. В языке Си определение массива можно написать только для МФР и МПР массивов. Динамические массивы создаются и уничтожаются с применением стандартных функций (malloc и др.). Память для массивов МФР можно выделить как в статической области памяти, так и в стеке. При этом память для всех массивов МФР, определения которых записываются вне функций, выделяется в статической области оперативной памяти компьютера. Память выделяется во время начала работы программы и освобождается во время завершения работы программы. Память для массивов МФР, определения которых записываются внутри функций, выделяется в стеке. Выделение памяти происходит в момент времени, когда управление достигает точки определения, а освобождается в момент времени, когда управление выходит из блока, в котором находится определение массива. Память для массивов МПР может быть выделена только в стеке. Рассмотрим этот вопрос применительно к одномерным и двумерным массивам.
Формат определения одномерного массива имеет следующий вид:
type name[expr];
Здесь type – тип элементов массива, name – имя массива, expr – выражение, определяющее количество элементов в массиве. Для МФР массивов expr должно быть константным выражением, а для МПР массивов должно быть неконстантным.
Формат определения двумерного массива имеет следующий вид:
type name[expr_row, expr_col];
Здесь type – тип элементов массива, name – имя массива, expr_row – выражение, определяющее количество строк, а expr_col – выражение, определяющее количество столбцов в массиве. Заметим, что для МФР массивов все выражения, стоящие в квадратных скобках, должны быть константными выражениями, для МПР массивов эти выражения должны быть неконстантными.
Приведем примеры определений массивов.
#define MSIZE 20 #define MROW 5 #define MCOL 4 int main(void) { int x[MSIZE]; double mat[MROW][MCOL]; int n; <ввод n > int z[n]; int mr, mc; <ввод mr, mc > double mt[mr][mc]; //... }
В приведенном выше программном коде объявлены два одномерных (x и z) и два двумерных массива (mat и mt). В одномерных массивах должны храниться элементы типа int, а в двумерных массивах – элементы типа double. Массивы x и mat являются массивами с фиксированными размерами. Размеры этих массивов задаются символическими константами. Размеры двух других массивов (z и mt) задаются с помощью переменных; n для массива z и двумя переменными mr и mc – для массива mt.
Отметим очень важное отличие между массивами МФР и МПР, которое состоит в том, что массивы МФР могут быть инициализированы во время определения, а массивы МПР не могут. Для выполнения инициализации необходимо в конце определения массива записать математический знак равенства ( = ), затем в фигурных скобках указать список инициализаторов. В простейшем случае количество инициализаторов должно совпадать с количеством элементов в массиве. Если инциализаторов оказалось меньше количества элементов массива, то компьютер дополняет этот список справа нулями. Ситуация, когда инициализаторов оказалось больше, чем это требуется, рассматривается как ошибка. Компилятор в этом случае выдает сообщение об ошибке. Приведем примеры инициализации массивов.
int x[4] = {1, 3, 5, 7};// x[0] == 1, x[1] == 3,x[2]== 5, // x[3] == 7 int y[4] = {2, 4}; // y[0] == 2, y[1] == 4, y[2] == 0, y[3]== 0 int z[4] = {0}; // Все элементы массива – нулевые int w[4] = {1, 2, 3, 4, 5} // Сообщение об ошибке int v[] = {1, 2, 3};// v[0] == 1, v[1] == 2, v[2] == 3 int v[2][3] = { {1, 2, 3}, // Первая строка {2, 4, 6} // Вторая строка };
