
- •Часть 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.5. Вложенные циклы
Вложенными циклами называется циклическая конструкция, содержащая не менее двух циклов. В целом такая конструкция является циклом (его принято называть внешним), в теле которого находятся один или несколько других циклов (их принято называть внутренними). Любой внутренний цикл может быть построен по системе вложенных циклов.
Пример Табулирование функции, содержащей суммы
Постановка задачи. Выполнить табулирование в n равноотстоящих точках на отрезке [xn, xk] функции
Решение. Обозначим через s1 левую сумму, а через s2 – правую сумму. При решении этой задачи необходимо организовать три циклических процесса. Первый из этих процессов – цикл табулирования заданной функции. Именно этот цикл определяет существо решаемой задачи. Два других цикла необходимы для вычисления сумм s1 и s2. Между последними циклами имеется существенное различие. Сумма s1 должна вычисляться заново для каждого нового значения x, а сумма s2 не должна. В связи с этим вычисление суммы s2 может быть вынесено из цикла табулирования.
Таким образом, программа решения этой задачи должна содержать два цикла: цикл для вычисления суммы s2, за которым должен следовать цикл табулирования, построенный по схеме вложенных циклов. Внешний цикл должен выполнять табулирование, а внутренний цикл – вычислять сумму s1.
#include<stdio.h> #include<math.h> int main(void) { double xn; printf(“%s”, “xn =”); scanf(“%lf”, &xn); double xk; printf(“%s”, “xk = ”); scanf(“%lf”, &xk); int n; printf(“n =”, “n = ”); scanf(“%d”, &n); int m; printf(“m =”); scanf(“%d”, &m); double s2=0 ; for(int k = 1; k <= m; k++)//область видимости переменной k s2 += k / (k + 1.0); //ограничена этим циклом double dx = (xk – xn)/(n-1); double x = xn; printf(“%5s%10s%15s\n”,”Num”, “X”, “Y”); for(int i = 1;i <= n; i++) // начало области видимости { // переменной i double s1 = 0; for(int j = 1; j <= m; j++)//область видимости //переменной j ограничена s1 += j /(j + x * x); // этим циклом double y = 2 * x + sqrt(x * x + s1 + s2); printf(“%5d%10.2f%15.2f\n”, I, x, y); x += dx ; } //конец области видимости переменной I getchar(); return 0; }
2.6. Работа с функциями
При разработке функций следует придерживаться следующих рекомендаций:
-
Функция, как правило, должна решать только одну задачу.
-
Обмен данными между функцией и точкой вызова должен выполняться через ее параметры и возвращаемое значение.
-
В функциях, как правило, не следует совмещать: a) ввод и обработку, б) обработку и вывод результатов.
2.6.1. Преобразование программы в функцию пользователя
Подобная задача нередко возникает на практике. Автору программы могут предложить оформить полученное решение в виде функции. Приведем пример.
Постановка задачи. Оформить в виде функции решение задачи о вычислении суммы цифр натурального числа (см. пример 2 раздела 2.4).
Решение. Разработка функции включает в себя разработку интерфейса и реализации. В рассматриваемом примере по существу требуется разработать только интерфейс функции. Элементы реализации в значительной степени могут быть заимствованы из программы, приведенной в разделе 2.4.
В разработке заголовка функции можно выделить следующие этапы.
-
Выбрать имя функции.
-
Определить состав входных и выходных данных.
-
Определить будет ли функция иметь возвращаемое значение.
-
Определить параметры функции.
Реализация функции в нашем случае сведется к выбору локальных переменных и переносу элементов управления из программы в тело функции.
Имя функции должно раскрывать существо решаемой задачи. В нашем случае подходящим именем будет summa_digits.
Перейдем к рассмотрению данных, которыми функция должна обмениваться с точкой вызова. К их числу будут относиться: исходное число n (входные данные) и вычисленная сумма (выходные данные).
В нашем случае результатом вычислений является скалярная величина, которую можно вернуть в точку вызова с помощью возвращаемого значения функции. Итак, функция должна возвращать значение типа int.
Входные данные функции представлены одним числом n. Отсюда следует, что функция должна иметь только один формальный параметр int n. Теперь можно написать заголовок функции.
int summa_digits (int n)
Разработку реализации функции начнем с определения ее локальных переменных. Кандидатами для включения в их число являются все локальные переменные функции main() исходной программы, а именно, n и s. В связи с тем, что переменная n стала формальным параметром, необходимость в локальной переменной n отпадает.
Все действия, составляющие существо алгоритма вычислений исходной программы, могут быть перенесены в тело разрабатываемой функции. Исключение составляют операции ввода и вывода. Необходимость в них отпадает. Их работа выполняется интерфейсными элементами функции.
Ниже показан программный код разработанной функции.
Int summa_digits(int n) { int s = 0; do { s += n % 10; }while(n /= 10); return s; }
Перейдем к разработке функции main(), которая должна вызывать функцию summa_digits(). Предположим, что обе функции будут находиться в одном исходном файле. В этом случае возникает вопрос о их взаимном расположении. Представляется целесообразным текст программы всегда начинать с функции main(). Дело в том, что знакомство читателя с программой должно начинаться всегда с функции main(). Анализ этой функции позволит программисту составить общее представление о программе. Такой порядок следования функций предусмотрен в программе, которая приведена ниже. Для обеспечения корректной компиляции вызова функции summa_digits() ее прототип приведен в начале программы.
#include<stdio.h> int summa_digits(int n); int main(void) { int n; printf(“%s”, “n=”); scanf(“%d”, &n); printf(“Summa digits = %d\n”, summa_digits(n)); getchar(); return 0; } int summa_digits(int n) { int s = 0; do { s += n % 10; }while(n /= 10); return s; }