
- •Часть 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.8.3. Присваивание несовместимых указателей
Несовместимыми являются указатели, которые указывают на объекты разных типов. Компиляторы языка Си ограничиваются выводом предупреждений. Результат выполнения операции присваивания для этого случая не определен. Рассмотрим следующий фрагмент программного кода, в котором имеет место присваивание несовместимых указателей.
double x = 10.5;
double *pd = &x;
float *pf = pd;
printf("*pf = %0.4g", *pf);
Третья строка кода содержит недопустимое присваивание. Vusual Studuo 2013 ограничивается выводом предупреждения: несовместимые типы - из "double *" в "float *". Программа, содержащая этот код, вышла на выполнение. На экране дисплея: *pf = 0.
1.37.8.4. Разыменование нулевого указателя
Разыменование нулевого указателя недопустимо. В приведенном ниже примере pi – внешняя переменная, которая по умолчанию инициализирована нулем (нулевым указателем). Разыменование такого указателя является ошибкой
int *pi=NULL; int main(void) {
*pi = 10; //Ошибка. pi – нулевой указатель
}
1.37.18.5. Несовместимость фактического и формального параметров указателей функции
В процессе редактирования программного кода формальные параметры указатели определения функции и фактические параметры вызова функции могут оказаться несовместимыми. Компилятор языка Си может ограничиться выводом предупреждения. Результаты же выполнения такой программы могут оказаться некорректными. Приведем примеры.
Пример 1. Компиляция клиентского кода функции swap(). В коде допущена ошибка. Оба фактических параметра имеют тип float*, а должны иметь тип double*
// код клиента
#include”swap.h”
int main(void) {
float a = 2, b = 3; swap(&a, &b); //…
}
//Файл swap.h void swap(double* p1, double* p2);
Компиляция этого кода в Visual Studio 2013 приводит к появлению предупреждения: warning C4133: функция: несовместимые типы - из "float *" в "double *".
Пример 2. Компиляция клиентского кода функции summa(), предназначенной для вычисления суммы элементов одномерного массива.
#include<stdio.h>
#include"summa.h"
int main(void)
{
float x[4] = { 1, 3, 5, 7 };
printf("summa(x, 4) = %10.3f\n", summa(x, 4));
return 0;
}
//Файл summa.h
float summa(const double x[], int n);
При компиляции этого фрагмента кода в Visual Studio 2013 получено следующее предупреждение: warning C4133: функция: несовместимые типы - из "float [4]" в "double *".
В обоих примерах вместо сообщений об ошибках компилятор ограничивается выводом предупреждений. Трудно ожидать, что результаты вычислений будут корректными.
1.37.8.6. Возврат из функции указателя на локальную переменную
Возврат из функции значения указателя на локальную переменную является ошибкой. Это связано с тем, что при завершении работы функции локальная переменная перестает существовать. Пример.
char* test(void) {
char buf[129] = "111 222"; return buf;
} int main(void) {
char* str = test(); puts(str); getchar(); return 0;
}
Ошибка, допущенная в этой программе, состоит в том, что указателю str присвоен адрес уже несуществующей локальной строки buf. Компилятор Code :: Blocks 13.12 ограничивается выводом предупреждения.
1.37.8.7. Висячий указатель
Появление висячих указателей обычно связывают с использованием динамической памяти. Указатель, установленный на динамическую память, становится висячим после освобождения функцией free() выделенной ему памяти. Устранение такого висячего указателя легко достигается присваиванием ему нулевого указателя. Это присваивание переводит указатель в состояние, которое можно назвать нерабочим. Нулевой указатель нельзя разыменовывать. Например,
// Фрагмент кода int* ptr = (int*)malloc(sizeof(int)); free(ptr); // Указатель ptr стал висячим ptr = NULL; // Перевод указателя в нерабочее состояние
Более сложная ситуация для устранения висячих указателей складывается в том случае, когда на одну и ту же динамическую память установлены два или более указателей. Дело в том, что в этом случае надо поставить в нерабочее состояние не только тот указатель, с помощью которого происходило освобождение памяти, но все другие. Например:
// Фрагмент кода int* ptr = (int*)malloc(sizeof(int)); int* ptr2 = ptr1; free(ptr); // Оба указателя стали висячими ptr = NULL; // Указатель ptr стал нерабочим ptr2 = NULL; // Теперь и ptr2 стал нерабочим.
В том случае, когда размер программного кода велик, легко можно забыть после освобождения памяти некоторые из указателей перевести в нерабочее состояние.