- •Основы алгоритмизации и программирования, язык Си
- •Введение
- •Блок-схема алгоритма Общие требования к блок-схеме алгоритма
- •Линейные и разветвляющиеся процессы
- •Циклические процессы
- •Итерационные процессы
- •Комментарии
- •Типы данных
- •Данные целого типа
- •Данные вещественного типа
- •Модификатор const
- •Переменные перечисляемого типа
- •Константы
- •Операции и выражения
- •Операция присваивания
- •Арифметические операции
- •Операции поразрядной арифметики
- •Логические операции
- •Операции отношения
- •Инкрементные и декрементные операции
- •Операция sizeof
- •Порядок выполнения операций
- •Приоритет операций
- •Преобразование типов
- •Операция приведения
- •Операция запятая
- •Ввод и вывод информации
- •Директивы препроцессора Директива #include
- •Директива #define
- •Понятие пустого и составного операторов
- •Условные операторы
- •Операторы организации цикла
- •Оператор цикла for
- •Оператор цикла while
- •Оператор цикла do … while
- •Вложенные циклы
- •Операторы перехода (break, continue, return, goto)
- •Примеры программ
- •Массивы Одномерные массивы
- •Примеры программ
- •Многомерные массивы (матрицы)
- •Примеры программ
- •Указатели Понятие указателя
- •Описание указателей
- •Операции с указателями
- •Связь между указателями и массивами
- •Массивы указателей
- •Многоуровневые указатели
- •Примеры программ
- •Символьные строки
- •Ввод/вывод строк.
- •Функции работы со строками.
- •Примеры программ
- •Функции
- •Прототип функции.
- •Определение функции.
- •Параметры функции
- •Параметры по умолчанию
- •Передача массива в функцию
- •Inline функции
- •Класс памяти
- •Автоматические переменные
- •Статические переменные
- •Регистровые переменные
- •Блочная структура
- •Примеры программ
- •Указатели на функции
- •Примеры программ
- •Рекурсия
- •Примеры программ
- •Аргументы в командной строке
- •Функции с переменным числом параметров
- •Примеры программ
- •Сортировка
- •Пузырьковая сортировка.
- •Шейкер сортировка
- •Сортировка вставкой
- •Сортировка выбором
- •Метод Шелла
- •Метод Хора
- •Структуры
- •Доступ к элементам структуры
- •Инициализация структур
- •Указатели на структуры.
- •Структуры и функции
- •Примеры программ
- •Поля бит
- •Объединения
- •Переменные с изменяемой структурой
- •Примеры программ
- •Организация списков и их обработка
- •Операции со списками при связном хранении
- •Построение обратной польской записи
- •Односвязный линейный список, очередь
- •Двусвязный линейный список
- •Циклический список, кольцо
- •Двусвязный циклический список
- •Примеры программ
- •Деревья
- •Потоки и файлы
- •Файлы Основные сведения о файловой системе
- •Организация посимвольного ввода и вывода
- •Определение конца файла feof()
- •Организация ввода и вывода строк
- •Удаление файлов
- •Дозапись потока
- •Позиционирование в файле
- •Текстовые и двоичные файлы
- •Функции fread() и fwrite()
- •Примеры программ
- •Хеширование
- •Схемы хеширования
- •Метод открытой адресации с линейным опробыванием
- •Метод цепочек
- •Машинное представление графов
- •Примеры программ
- •Литература
Функции
Принцип программирования на языке С(С++), как и в ряде других языков программирования, основан на понятии функции. В рассмотренных ранее программах мы уже пользовались функциями. К их числу относятся функции ввода/вывода, работы с символьными строками и ряд других. Эти функции являются системными, в то же время мы разработали несколько функций, использующих вышеназванные и озаглавленных одним общим именем main(). Несколько подробнее остановимся на вопросе, как создавать свои собственные функции и делать их доступными для функции main(), а также друг для друга.
Что такое функция? Функция - самостоятельная единица программы, разработанная для выполнения некоторого функционально законченного действия. Связь между функциями осуществляется через аргументы, возвращаемые значения и внешние переменные. Если внутри функции используются уже определенные ранее имена, то это соответствует неявной передаче информации в функцию. В исходном файле функции разрешается располагать в любом порядке, исходную программу можно подразделять на произвольное число файлов. Следует различать понятия «прототип функции» и «определение функции».
Прототип функции.
Прототип функции – это ее имя с указанием типа возвращаемого результата и перечислением в круглых скобках (через запятую) типов передаваемых параметров. Прототип функции завершается точкой с запятой. Например:
double step(float x, int n); int min(int , int );
char * prod(char *, int k);
Прототип является предварительным объявлением данных функции: имени функции, количества и типов параметров, типа результата. Прототип необходим компилятору для того, чтобы проконтролировать корректность вызова функции и в необходимых случаях, выполнить преобразование аргументов к типу принимаемых параметров, а также сгенерировать корректный возвращаемый результат.
Имена параметров в прототипе можно опустить, однако рекомендуется их указывать. Компилятор будет использовать имена параметров при выдаче диагностических сообщений о несоответствии типов аргументов и параметров. В том случае, когда в функцию не передаются аргументы, в прототипе в круглых скобках вместо списка параметров записывается слово void либо ничего не указывается.
Определение функции.
Определение (или описание) функции – это ее полная реализация. Определение функции может располагаться в любом месте программы, но определение одной функции не может вмещать в себя определение другой функции, но может содержать ее прототип, если она вызывает эту функцию.
Если определение некоторой функции располагается ниже точки ее вызова или в другом файле, то для такой функции выше точки ее вызова обязательно должен быть помещен прототип. В соответствии с синтаксисом языка СИ определение функции имеет следующую форму:
[спецификатор_класса_памяти] [спецификатор_типа] имя_функции
([список_формальных_параметров])
{ тело_функции }
Необязательный спецификатор_класса_памяти задает класс памяти функции, который может быть static или extern. Спецификатор_типа функции задает тип возвращаемого результата. Результат может быть любого стандартного типа, а также типа, определенного пользователем (структура, объединение, класс и др.). Функция возвращает явно только одно значение указанных типов. Допускается реализация функций, не имеющих параметров и/или не возвращающих никаких значений. Если спецификатор_типа не задан, то предполагается, что функция возвращает значение типа int.
Передача (возврат) значения из вызванной функции в вызывающую осуществляется посредством использования оператора возврата return, имеющего следующий вид:
return выражение; или return (выражение);
Выражение может являться как некоторой константой, так и вычисляемым выражением:
return -12; или return (-12);
return i+5/j; или return (i+5/j);
Оператор return в теле функции может встречаться более одного раза. При его встрече происходит прекращение выполнения функции и осуществляется передача управления очередной инструкции (команде) в вызывающей функции. Если в операторе return отсутствует выражение, а вызваемая функция должна вернуть некоторое значение то компилятор генерирует ошибку. При отсутствии оператора return функция будет выполнена до конца. В этом случае функция также явно не возвращает результат, перед именем такой функции в ее прототипе и в ее определении должно быть записано слово void.
Если тип выражения в операторе return не соответствует типу значения, возвращаемому функцией, то будут выполнены автоматически соответствующие преобразования типа выражения к типу возвращаемого функцией значения. В случае невозможности таких преобразований выводится сообщение об ошибке.
Функция не может в качестве результата возвращать массив любого типа, но может передать указатель на такой массив. Это связано с тем, что в С(С++) нет операции присваивания массивов.
Спецификатор типа возвращаемого функцией результата должен быть указан перед именем функции в операторе прототипа и в определении функции. Отдельные части спецификации могут отсутствовать. При отсутствии типа возвращаемого результата предполагается, что функция возвращает значение типа int.
Ниже приводится пример программы, в которой из функции main() производится вызов поочередно двух функций (fun1, fun2):
#include <stdio.h>
int fun1(int , int ); // прототип функции fun1
int fun2(int *, int *); // прототип функции fun2
void main()
{ int a,b,c;
scanf("%d%d",&a,&b);
c=fun1(a,b); // вызов функции fun1
printf("\n 1: результат %d - %d = %d",a,b,c);
c=fun2(&a,&b); // вызов функции fun2
printf("\n 2: результат %d - %d = %d",a,b,c);
}
fun1(int a,int b)
{ a--; // измененное локальное значение переменной a
return a-b; // в main передается значение разности a-b
}
fun2(int *a,int *b)
{ (*a)++; // изменяется значение по адресу a
return *a-*b;
}
Функция fun1 принимает в качестве аргументов значения переменных a и b, копируя их в локальные переменные a и b. Изменение переменной a в функции не приводит к изменению значения переменной a описанной в main.
Функция fun2 принимает адреса переменных a и b, копируя их в указатели. Изменение значения по адресу переменной a приводит к изменению значения переменной а в функции main.
Любая функция, может завершаться при вызове системной функции abort() или exit(n) , где n – целое число, являющееся кодом завершения. Код завершения обычно используется программой, которая породила текущий процесс. В этом случае прекращается выполнение всей программы, автоматически закрываются все открытые файлы и освобождаются все области динамической памяти.