Функция, определённая в файле до любого обращения к ней, не требует отдельного объявления. В противном случае (когда функция определена после обращения к ней или в другом модуле) до первого вызова функция должна быть объявлена с помощью прототипа.
Общий вид прототипа следующий:
тип имя_функции([список_формальных_параметров])[,
имя_функции2([список2_формальных_параметров])];
Прототип (объявление, описание, заголовок, сигнатура) функции в С++ указывает компилятору тип значения, возвращаемого функцией, количество параметров, которые передаются функции, типы этих параметров и ожидаемый порядок их следования. Компилятор использует прототип функции для проверки корректности вызовов функции. Компилятор игнорирует имена переменных, упомянутые в списке формальных параметров прототипа функции.
В одном объявлении можно указать несколько функций, если они имеют один тип возвращаемого значения (каждую функцию со своим списком параметров в скобках):
// Листинг 9.1
|
|
#include <iostream>
#include <conio.h>
using namespace std;
// Компилятор игнорирует имена переменных
// в прототипе функции
// Прототип, имя параметра указывать не нужно
bool IsDigit(char);
// Прототипы функций
void f1(int), f2(double), f3(int, int);
int main() {
if (IsDigit('w')) cout << " Digit " << endl;
else cout << " Not digit " << endl;
if (IsDigit('5')) cout << " Digit " << endl;
else cout << " Not digit " << endl;
// 119 – код символа 'w', 53 – символа '5'
if (IsDigit(119)) cout << " Digit " << endl;
else cout << " Not digit " << endl;
if (IsDigit(53)) cout << " Digit " << endl;
else cout << " Not digit " << endl;
f1('w');
f1(100);
f1(-3.54);
f2('w');
f2(100);
f2(-3.54);
f3(9, 5);
f3(2.5, 2.5);
return 0;
}
bool IsDigit(char c) {
cout <<"sizeof(c) = "<< sizeof(c) <<" c = "<< c;
return (c >= '0' && c <= '9');
}
void f1(int n) {
cout <<"1 sizeof(n) = "<< sizeof(n)
<< " n = " << n << endl;
}
void f2(double d) {
cout <<"2 sizeof(d) = "<< sizeof(d)
<< " d = " << d << endl;
}
void f3(int n, int m) {
cout <<"3 sizeof(n) = "<< sizeof(n)
<<" n*m = "<<n*m<< endl;
}
Результат выполнения программы:
sizeof(c) = 1 c = w Not digit
sizeof(c) = 1 c = 5 Digit
sizeof(c) = 1 c = w Not digit
1 sizeof(n) = 4 n = 119
1 sizeof(n) = 4 n = 100
1 sizeof(n) = 4 n = -3
2 sizeof(n) = 8 d = 119
2 sizeof(n) = 8 d = 100
2 sizeof(n) = 8 d = -3.54
3 sizeof(n) = 4 n*m = 45
3 sizeof(n) = 4 n*m = 4
Вызов функции, который не соответствует прототипу функции, ведёт к синтаксической ошибке. Синтаксическая ошибка возникает также в случае отсутствия согласования между прототипом и определением функции.
Различают:
- список формальных параметров – список параметров в определении (и объявлении) функции;
- список фактических параметров – список значений, передаваемых в функцию при её вызове.
|
|
При вызове функции типы и порядок следования параметров в списке фактических параметров должны совпадать с типами и порядком следования формальных параметров в списке объявления, определения функции.
Рекурсия
Рекурсивной называют функцию, которая в своём теле вызывает саму себя (прямая рекурсия). Когда две или более функций вызывают друг друга, возникает косвенная рекурсия.
Пример: вычисление факториала числа (0! = 1 и 1! = 1)
n! = 1×2×3×(n–1)×n
// Листинг 9.2
#include <iostream>
using namespace std;
int factorial(int); // Прототип функции
int main() {
int k = 4;
cout <<"k="<< k <<"; k! ="<< factorial(k) <<endl;
return 0; }
int factorial(int n) {
if (n < 0) {
cout << "ERROR!" << endl; return -1;
}
// Рекурсивный вызов
return (n > 1)? n*factorial(n–1): 1;
}
Каждое обращение к функции при рекурсии сопровождается организацией в стеке нового полного набора её локальных переменных и переданных ей параметров. Поэтому рекурсивными функциями следует пользоваться с осторожностью, оценивая глубину рекурсии (иначе возможно переполнение стека).
Очень важно при организации рекурсивных функций предусмотреть проверку условия выхода из рекурсии. Иначе можно «улететь» в бесконечную рекурсию. Любую рекурсивную функцию можно реализовать без использования рекурсии.
Достоинство рекурсии – компактная запись.
Недостатки рекурсии – расход времени и памяти на повторные вызовы функции и передачу ей копий параметров, а также опасность переполнения стека.