Язык программирования Си для персонального компьютера
Шрифт:
Однако, помимо явного объявления, функция может быть объявлена неявно, по контексту ее вызова. Неявное объявление имеет место всякий раз, когда функция вызывается без предварительного объявления или определения. В этом случае компилятор языка Си считает, что вызываемая функция имеет тип возвращаемого значения int и класс памяти extern. Определение функции, если оно имеется далее в том же самом исходном файле, может переопределить тип возвращаемого значения и класс памяти.
Тип возвращаемого значения функции, указанный в предварительном
Если функция, тип возвращаемого значения которой не int, вызывается до ее определения или объявления, то компилятор сообщает об ошибке.
Основное назначение предварительного объявления состоит в задании типов и числа аргументов, ожидаемых в вызове функции. Список типов аргументов позволяет компилятору осуществить контроль типов аргументов при вызове функции. Если предварительное объявление отсутствует, то программист сам должен следить за соответствием типов между фактическими аргументами и формальными параметрами. Более детально контроль типов рассмотрен в разделе 6.4.1 "Фактические аргументы".
Пример:
main(void)
{
int а = 0, b = 1;
float х = 2.0, у = 3.0;
double realadd (double, double);
a = intadd(a, b);
x = realadd(x, y);
}
intadd(int a, int b)
{
return (a + b);
}
double realadd(double x, double y)
{
return (x + y);
}
В примере функция intadd объявлена неявно с типом возвращаемого значения int, так как она вызнана до своего определения. Компилятор не проверит типы аргументов при вызове функции intadd, поскольку список типов аргументов для нее не задан.
Функция realadd возвращает значение типа double. В функции main имеется предварительное объявление функции realadd. Тип возвращаемого значения (double), заданный в определении, соответствует типу возвращаемого значения, заданному в предварительном объявлении. В предварительном объявлении также определены типы двух параметров функции realadd. Типы фактических аргументов соответствуют типам, заданным в предварительном объявлении, и также соответствуют типам формальных параметров в определении функции realadd.
Вызов функции
Вызов функции передает управление и фактические аргументы (если они есть) заданной функции. Синтаксически вызов функции имеет следующий вид:
<выражение>([<список выражений>])
<Выражение> вычисляется, и его результат интерпретируется как адрес функции. Выражение должно иметь тип функция.
<Список выражений>, в котором выражения следуют через запятую, представляет собой перечень фактических аргументов,
При выполнении вызова функции происходит присвоение значений фактических аргументов формальным параметрам. Перед этим каждый фактический аргумент вычисляется, над ним выполняются необходимые преобразования, и он копируется в стек. Первый фактический аргумент соответствует первому формальному параметру, второй — второму и т. д. Все аргументы передаются по значению, только массивы — по ссылке.
Вызванная функция работает с копией фактических аргументов, поэтому никакое изменение значений формальных параметров не отразится на значениях аргументов, с которых была сделана копия.
Передача управления осуществляется на первый оператор тела функции. Выполнение оператора return в теле функции возвращает в точку вызова управление и, возможно, значение. В отсутствие оператора return управление возвращается по достижении завершающей фигурной скобки тела функции. В этом случае возвращаемое значение не определено.
Примечание. Порядок вычисления выражений, представляющих аргументы вызова функции, не определен в языке Си, поэтому наличие побочных эффектов в этих выражениях может привести к непредсказуемым результатам. Гарантируется только то, что все побочные эффекты будут вычислены до передачи управления в вызываемую функцию.
<Выражение> должно ссылаться на функцию. Это означает, что функция может быть вызвана не только по идентификатору, но и через любое выражение, имеющее тип указателя на функцию.
Вызов функции синтаксически напоминает ее объявление. При объявлении функции сначала записывается ее имя, а затем список типов аргументов в скобках. При вызове также записывается имя функции, а за ним следует список выражений в скобках.
Аналогичным образом функция вызывается через указатель. Предположим, что указатель на функцию объявлен следующим образом:
int (*fpointer)(int, int);
Идентификатор fpointer именует указатель на функцию с двумя аргументами типа int и возвращаемым значением типа int. Вызов функции в этом случае будет выглядеть так:
extern int f (int, int);
fpointer = &f; /*знак & необязателен */
(*fpointer)(3,4); /* можно и просто fpointer(3,4); */
Примеры:
/* пример 1 */
double *realcomp(double, double);
double a, b, *rp;
rp = realcomp(a, b);
/* пример 2 */
main
{
long lift(int), slep(int), drop(int);
void work(int, long (*)(int));
int select, count;
.
.
.
switch(select) {
case 1: work(count, lift); break;
case 2: work(count, step); break;
case 3: work(count, drop); break;
default: break;
}
void work(int n, long (*func)(int))
{
int i;
long j;
for(i = j = 0; i < n; i++)