
- •Часть 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.Замена всех отрицательных чисел двоичного файла нулями
- •Литература
2.7.4. Создание нового массива копированием положительных элементов из исходного массива
Постановка задачи. Имеется массив in, состоящий из n элементов типа double. Требуется создать новый массив out, скопировав в него все положительные элементы массива in.
Решение. Выберем для функции имя copy_pos_elems_arr. Входными данными для функции являются: неизменяемый массив in и количество его элементов n. Выходными данными должны быть: создаваемый массив out и его количество элементов. Массив придется возвращать через параметр функции, а количество элементов выходного массива можно вернуть через возвращаемое значение функции. Входные данные следует передать в функцию через ее параметры. В теле функции нужно объявить счетчик положительных элементов и организовать арифметический цикл. В цикле должна присутствовать инструкция if для выделения положительных элементов, их копирования и подсчета.
Программный код разработанной функции.
int copy_pos_elems_arr(const double in[], int n, double out[]) { int n_pos = 0;//n_pos – счетчик положительных элементов for(int i = 0; i < n; i++) { if(in[i] > 0) { out[n_pos++] = in[i];//требуется постинкремент } } return n_pos; }
2.7.5. Значения наибольшего и наименьшего элементов массивов
Постановка задачи. Имеется одномерный массив, состоящий из n элементов типа double. Требуется вычислить максимальное и минимальное значения элементов этого массива.
Решение. В рассматриваемой задаче можно выделить две подзадачи. Первая из них – вычисление максимального значения, а вторая – вычисление минимального значения. Принято считать, что каждая функция должна быть предназначена для решения только одной задачи. Основываясь на этих соображениях, для решения нашей задачи следует написать две функции. Представляется целесообразным привести и второй вариант, в котором решение получается с помощью одной функции пользователя. Это позволит сравнить два решения.
Вариант 1. Две пользовательские функции.
Интерфейс функций. Имена функций, отражающие целевое назначение функций: max_arr и min_arr. Входные данные – неизменяемый массив и количество элементов массива. Для каждой функции результат вычислений является скаляром, его можно вернуть в точку вызова через возвращаемое значение функции.
Реализация функций. В теле каждой из функций должен быть организован арифметический цикл для просмотра элементов массива. До входа в цикл необходимо объявить вспомогательную переменную. Назовем ее max для первой функции (min – для второй). Эта переменная после просмотра всех элементов массива должна содержать искомый результат. При своем объявлении эта переменная может быть инициализирована значением первого элемента массива, тогда начальным значением счетчика цикла должна быть 1. В теле цикла переменная max (min) должна сравниваться со значением текущего элемента массива. Если ar[i] > max (ar[i] < min), необходимо выполнить присваивание max = ar[i] (min = ar[i]). В конце тела функции надо вернуть окончательное значение переменной max (min)
Программный код разработанных функций.
double max_ar(const double ar[], int n) { double max = ar[0]; for(int i =1; i < n; i++) if(ar[i] > max) max = ar[i]; return max; }
double min_ar(const double ar[], int n) { double min = ar[0]; for(int i =1; i < n; i++) if(ar[i] < min) min = ar[i]; return min; }
Вариант 2. Одна пользовательская функция, для вычисления максимального и минимального значений элементов массива.
В этом варианте функция должна вернуть два результата: максимальное и минимальное значение. Здесь возможны два подварианта. В первом из них одно значение возвращается через возвращаемое значение функции, а второе с помощью указателя. Второй подвариант: оба значении возвращаются с помощью указателей. Выберем первый подвариант.
Программный код пользовательской функции для вычисления максимального и минимального значения элементов массива
double max_min_arr(const double ar[], int n, double* pmin) { double max = ar[0]; *pmin = ar[0];
for(int i = 1; i < n; i++) { if(ar[i] > max) max = ar[i]; if(ar[i] < *pmin) *pmin = ar[i]; } return max; }
Программный код тестовой программы
#define MSIZE 20 int main(void) { double x[MSIZE] = {1, -2, 100, 50}; double mn; double mx = max_min_arr(x, 4, &mn); printf(“max = %0.4g min = %0.4g\n”, mx, mn); return 0;
}
Программа выводит ожидаемый результат:
max = 100 min = -2
Недостатком второго варианта решения является ухудшение повторного использования кода. Предположим, что у программиста в дальнейшем могут встречаться ситуации, когда необходимо будет вычислять либо только минимальное, либо только максимальное значение. Возникает вопрос, легко ли будет приспособить функцию max_min_arr() для таких вычислений. Если окажется ненужным максимальное значение, его можно игнорировать, т.е. работать с функцией, как будто она не имеет возвращаемого значения. Сложнее обстоит дело в том случае, когда ненужным будет минимальное значение. Дело в том, то игнорировать «ненужный» аргумент в вызове функции нельзя. Представляется, что программисту, который часто встречается с необходимостью вычислять максимальное и минимальное значения элементов массива целесообразно иметь все три варианта пользовательских функций.