• Любая программа на языке Си состоит из функций, одна из которых должна иметь имя main (с нее начинается выполнение программы). Функция – это именованная последовательность описаний и операторов, выполняющая какое-либо законченное действие.
Чтобы использовать в программе свою собственную функцию, необходимо выполнить три действия: • задать прототип функции; • вызвать функцию в необходимом месте, например, в функции main() • дать определение функции.
Рассмотрим пример функции, которая определяет минимум трех значений: // Функция, возвращающая минимальное значение трех чисел float min(float x, float y, float z) { float Min; if((x <= y) && (x <= z) Min= x; else if((y <= x) && (y <= z) Min= y; else Min= z; return Min; }
// Использование функции min() void main(void) { float x, y, z; // значения, задаваемые пользователем float Min; // минимальное значение // Вводим три значения cin >> x >> y >> z; //предположим ввели 10, 3, 5 //Первый вариант использования функции min() Min= min(x, y, z); // 3 = min(10, 3, 5) cout << x << y << z;
//Второй вариант использования функции min() Min= min(14, y+3, exp(z-4)); // 2. 72 = min(14, 6, 2. 72) cout << min;
// Третий вариант использования функции min() cout << min(11, 12, 2); // 2 = min(11, 12, 2)
В приведенном выше примере определение функции min() (соответственно и объявление) выполнено до ее использования в функции main(). Однако это не всегда удобно (по сути, функция main() всегда будет последней), а в некоторых случаях просто невозможно.
Пример когда невозможно определить функцию раньше ее использования. // Объявление и определение функции f 1 void f 1(void) {f 2(); // вызов функции f 2() } // Объявление и определение функции f 2 void f 2(void) {f 1(); // вызов функции f 1() }
Разрешить данную проблему можно, используя предварительное объявление функции: // прототип функции f 1 void f 1(void); // прототип функции f 2 void f 2(void); // Объявление и определение функции f 1 void f 1(void) {f 2(); // вызов функции f 2() } // Объявление и определение функции f 2 void f 2(void) {f 1(); // вызов функции f 1() }
Предварительное объявление функции называется прототипом функции. Рекомендуется использовать прототипы функций всегда, т. к. это дает следующие преимущества: – не нужно следить за порядком вызова функций; – формируется перечень реализуемых вами функций.
Прототип функции — это заголовок функции, который заканчивается точкой с запятой. Прототипы всех функций, используемых в программе, обычно записывают в начале текста программы до определения функций
Прототип необходим для того, чтобы у компилятора была полная информация о типе результата работы функции и о списке параметров, передаваемых в функцию. Когда в тексте программы встретится обращение к функции, то компилятор проверяет правильность её вызова, сверяясь с информацией из прототипа.
// Прототип функции, возвращающей минимальное значение трех чисел float min(float x, float y, float z); // Использование функции min() void main(void) {cout << min (11 , 12 , 2); //2= min(11, 12, 2) } //Описание чисел функции, возвращающей минимальное значение float min(float x, float y, float z); {float Min; if ((x <= y) && (x <= z) Min= x; else if((y <= x) && (y <= z) Min= y; else Min= z; return Min; } трех
Вызов функции возможен в любой функции, где это потребуется. Если тип результата функции — void, то вызов записывается отдельным оператором, в остальных случаях — в выражении того же типа, что и тип результата функции.
Определение функции состоит из её заголовка и тела, записанного в виде блока. Допустимо записывать определение функций в любой последовательности. Нельзя определять функцию внутри другой функции (С/С++ — это не Паскаль!).
Возвращаемое значение Формально функция может возвращать только одно значение. Для возвращения значения из функции и одновременного завершения функции используется оператор return. Bозвращаемое значение может иметь любой тип данных, кроме массива (тип возвращаемого значения указывается в объявлении функции).
Pассмотрим две реализации одной и той же функции, возвращающей значение Pi : // Функция, возвращающая значение Pi double Pi() { return 3. 14159265358979; } // Функция, возвращающая значение Pi double Pi() { return 2*asin(1. 0); }
Параметры функции Механизм параметров является основным способом обмена информацией между вызываемой и вызывающей функциями. Выделяют формальные и фактические параметры функции. Параметры, перечисленные в описании функции, называются формальными, а записанные в операторе вызова функции – фактическими.
//Первый вариант Min= min(x, y, z); // 3 = min(10, 3, 5) cout << Min; //Второй вариант Min= min(14, y+3, exp(z-4)); // 2. 72 = min(14, 6, 2. 72) cout << Min; // Третий вариант cout << min(11, 12, 2); // 2 = min(11, 12, 2)
В данном примере формальными параметрами функции min() являются ее локальные переменные x, y и z, а фактическими – переменные, константы и выражения, принадлежащие функции main(): – x, y, z – первый вариант – 14, y+3, exp(z-4)- второй вариант – 23, 2, 11 третий вариант параметры при этом передаются по значению
Передача данных по значению Этот способ передачи данных в подпрограмму является основным и действует по-умолчанию. Фактический параметр вычисляется в вызывающей функции и его значение передаётся на место формального параметра в вызываемой функции. На этом связь между фактическим и формальным параметрами прекращается.
В качестве фактического параметра можно использовать константу, переменную или более сложное выражение. Передача данных по значению пригодна только для простых данных, которые являются входными параметрами. Если параметр является выходным данным или массивом, то передача его в функцию по значению не приемлема.
Пример. Даны два числа, хранящиеся в переменных a и b. Используя функцию, выполнить обмен содержимого ячеек этих переменных.
void Obmen(double x, double y); //прототип int main() { double a = 2. 5, b = 3. 1; cout << "Do Obmen: a=" << a << " b=" << b << endl; Obmen(a, b); // использование функции cout << "Posle Obmen: a=" << a << " b= « << b << endl; return 0; }
void Obmen(double x, double y) //сама функция { double c; cout << "Function Obmen start: n x=" << x << " y=" << y << endl; c = x; x = y; y = c; cout << "Function Obmen end: n x=" << x << " y=" << y << endl; }
Результаты выполнения программы: Do Obmen: a=2. 5 b=3. 1 Function Obmen start: x=2. 5 y=3. 1 Function Obmen end: x=3. 1 y=2. 5 Posle Obmen: a=2. 5 b=3. 1
Вывод на экран значений переменных показывает, что данные в функцию переданы правильно, перестановка в функции произведена, но это ни как не отразилось на значениях исходных переменных a и b после выхода из функции Obmen(). Этот пример наглядно показывает, что через параметры, передаваемые по значению, нельзя вернуть результаты работы функции.
Передача данных по адресу При передаче по адресу функция работает с фактическими параметрами через их адреса, следовательно, она может изменять значения фактических параметров. В случае передачи данных по адресу фактический параметр может быть только переменной (константа или выражение не имеют адреса!).
Механизм передачи параметров по адресу фактически позволяет возвращать из функции более чем одно значение. В этом случае одна и та же переменная будет входной и выходной для функции. Кроме того, механизм передачи параметров по адресу снижает затраты памяти.
Пример 3. Даны два числа, хранящиеся в переменных a и b. Используя подпрограмму, выполнить обмен содержимого ячеек этих переменных. Данные передадим по адресу. Они будут в этой задаче и входными, и выходными данными. Для контроля изменения содержимого ячеек памяти будем выводить на экран монитора промежуточные данные.
#include <iostream> using namespace std; void Obmen(double *x, double *y); int main() { double a = 2. 5, b = 3. 1; cout << "Do Obmen: a=" << a << " b=" << b << endl; Obmen(&a, &b); cout << "Posle Obmen: a=" << a << " b=" << b << endl; return 0; }
void Obmen(double *x, double *y) { double c; cout << "Function Obmen start: n *x=" << *x << " *y=" << *y << endl; c = *x; *x = *y; *y = c; cout << "Function Obmen end: n *x=" << *x << " *y=" << *y << endl; }
Результаты выполнения программы: Do Obmen: a=2. 5 b=3. 1 Function Obmen start: *x=2. 5 *y=3. 1 Function Obmen end: *x=3. 1 *y=2. 5 Posle Obmen: a=3. 1 b=2. 5
Массивы в функцию всегда передаются по адресу. Для массива это вообще единственный способ передачи данных в языках С/С++. При передаче массива в функцию передается указатель на его первый элемент. При этом информация о количестве элементов массива теряется, и следует передавать его размерность через отдельный параметр. Исключением являются строки (массивы символов), в которых длина определяется нульсимволом.
Пример. Написать программу, которая будет вызывать функцию для вывода на печать элементов массива.
void print(int x[], int n); int main() { const int n = 5; int x[n] = {3, 5, 1 , 7, 4}; print(x, n); return 0; } void print(int x[], int n) { cout << "Massiv: " << endl; for(int i = 0; i < n; i++) cout << x[i] << endl; }
При передаче многомерных массивов все размерности должны передаваться в качестве параметров. Внутри функции массив интерпретируется как одномерный, а его индекс пересчитывается в программе.