
- •Часть 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.37.4. Указатели и квалификатор restrict
По словам Герберта Шилдта, автора книги [6]: “ Одной из самых важных новинок стандарта С99 является квалификатор restrict (ограниченный)”. Он применяется только к указателям. Предполагается, что такой указатель является единственным указателем на объект, на который он установлен. Этот квалификатор в стандарте С99 был добавлен в прототипы многих библиотечных функций, определённых еще в стандарте С89.
1.37.5. Связь между указателями и массивами
В языке Си массивы и указатели тесно между собой связаны и часто могут быть использованы взаимозаменяемо. Имя массива можно использовать как константный указатель. Пусть имеются два однотипных массива:
int a[5], b[5];
Имена массивов a и b, рассматриваемые как константные указатели, будут иметь тип int* const. Таким образом, имя одномерного массива является константным указателем на первый элемент массива. В связи с этим оператор a = b не должен успешно компилироваться. Это объясняет невозможность использования в языке Си оператора присваивания для копирования массивов.
Имя двумерного массива также является константным указателем, но более сложного типа по сравнению с именем одномерного массива. Дело в том, что, строго говоря, в языке Си многомерных массивов нет. В языке Си многомерные массивы моделируются с помощью одномерных массивов, элементами которых являются другие массивы. Обратимся к случаю двумерных массивов. Пусть имеется следующее определение массива:
double m[3][4];
Компилятор языка Си рассматривает такой массив, как одномерный, рассчитанный на хранение трех элементов, которыми являются одномерные массивы. Каждый из этих массивов имеет тип double[4]. С учетом этого имя массива является константным указателем типа double (* const p)[4]. Скобки здесь необходимы, иначе это будет объявлением массива указателей.
Другим проявлением связи между указателями является то положение, что любой указатель, установленный на элемент массива, может быть индексирован. Например,
int a[5]; int*p; p = a; a[2] = 10; p[2] = 20; // Допустимо. теперь a[2] == 20
Следует учитывать, что указатели можно использовать для выполнения любых операций, в которых применяется оператор индексирования. Пусть имеются одномерный массив int a[5] и двумерный массив int m[2][3]. Для рассматриваемого случая выражение a[1] можно заменить выражением *(a + 1), а выражение m[1][2] – заменить выражением *(*(m + 1) + 2).
1.37.6. Указатели – параметры функций
Применение параметров-указателей в языке Си позволяет программисту вернуть в точку вызова функции результаты вычислений, которые в ней получены. Эта возможность обусловлена тем, что указатели позволяют организовать связь между функцией и памятью, выделенной в вызывающей функции. Хорошим примером такого использования является функция swap().
Пример. Обмен значений двух переменных типа double.
Постановка задачи. Даны две переменные x и y типа double. Написать функцию, позволяющую выполнить обмен значений этих переменных.
Решение. Разработка интерфейса. Предположим, что в точке вызова функции объявлены две переменные типа double c именами x и y. Для получения доступа к этим переменным разрабатываемая функция должна иметь два параметра – указателя типа double*. Учитывая, что возврат результатов берут на себя параметры, сама функция может не иметь возвращаемого значения. С учетом этого заголовок функции будет иметь вид:
void swap(double* p1, double* p2);
Разработка алгоритма. Для организации обмена в теле функции swap необходимо выполнить три действия:
1. Объявить локальную переменную double temp и переписать в нее значение переменной x, разыменовав указатель p1.
2. Скопировать содержимое переменной у в переменную x. Учесть, что синонимом x является *p1, а синонимом y – *p2.
3. Скопировать в переменную y значение x, сохраненное в temp.
Программный код разработанной функции и клиентский код.
void swap(double *p1, double *p2) { double temp = *p1; //В temp сохраняем значение «x» *p1 = *p2; //Копируем в «x» значение «y» *p2 = temp;//Копируем в «y» значение «x», //сохраненное в temp } // Клиентский код int main(void) { double x = 2, y = 3; swap(&x, &y); //На время выполнения вызова swap() //адрес переменной x копируется
//в параметр p1,а адрес переменной y - в
//параметр p2 }