3. Возвращаемое значение
Механизм возврата из функции в вызвавшую ее функцию реализуется оператором
return [выражение];
Функция может содержать несколько операторов return (это определяется потребностями алгоритма) – см. пример 1.2. Если функция описана как void, выражение не указывается. Оператор return можно опускать для функции типа void, если возврат из нее происходит перед закрывающей }, и для функции main. Выражение, указанное после return, неявно преобразуется в типу возвращаемого функцией значения и передается в точку вызова функции.
Пример 6:
int f1() {return 1;} // правильно
void f2() {return 1;} //неправильно, f2 не должна возвращать значение
double f3() {return 1;} //правильно, 1 преобразуется к типу double
4. Параметры функции
Функция должна иметь доступ к данным в вызывающей ее программе. Эту проблему можно решить, сделав необходимые данные глобальными. Но такой путь неудобен и чреват ошибками, т.к. глобальные данные доступны всем функциям, поэтому лучше для этой цели использовать механизм параметров. Механизм параметров является основным способом обмена информацией между вызываемой и вызывающей функциями.
Формальные параметры перечисляются при описании функции в ее заголовке и могут использоваться внутри нее наряду с локальными переменными, описанными в теле функции.
При вызове фактические параметры передаются в функцию и используются в ее работе. Часто фактические параметры функции называют ее аргументами. При вызове функции количество, порядок и типы фактических параметров должны соответствовать формальным параметрам в определении функции.
Можно выделить следующие этапы вызова функции:
проверка соответствия фактических и формальных параметров функции,
присваивание фактических значений формальным параметрам функции,
передача управления на первый оператор функции.
Т.е. при вызове функции в первую очередь вычисляются выражения, стоящие на месте аргументов, затем в стеке выделяется память под формальные параметры функции в соответствии с их типом, и каждому из них присваивается значение соответствующего аргумента. При этом проверяется соответствие типов и при необходимости выполняются их преобразования. При несоответствие типов выдается диагностическое сообщение.
Допускается использование функций без аргументов и функций, не возвращающих никаких значений.
Существует два способа передачи параметров в функцию – по значению и по адресу.
При передаче по значению в стек заносятся копии значений аргументов, и операторы функции работают с этими копиями. Доступа к исходным значениям параметров у функции нет, а следовательно, и нет возможности их изменить. Функция меняет лишь временную копию (формальный параметр), значение которой теряется при выходе из функции.
При передаче по адресу в стек заносятся копии адресов аргументов, а функция осуществляет доступ к ячейкам памяти по этим адресам и может изменить исходные значения аргументов. Передача по адресу связана с использованием указателей.
По умолчанию параметры любого типа, кроме массива и функции, передаются в функцию по значению.
Пример 7. Функция power() возводит base в n-ую степень
…
int power (int base, int n)
{int p;
for (p=1; n>0; --n)
p=p*base;
return p;
}
void main()
{int i, p, dec;
for (i=1, dec=10, p=0; i<5; i++, dec=dec*i)
p=p + power(dec, i);
…
}
Обратим внимание на одноименные переменные р в вызывающей программе и функции power(), это две разные переменные – локальные переменные этих двух функций. Переменная р в функции power() маскирует (или исключает) переменную р в вызывающей программе.
При вызове power(dec, i) создаются две локальные переменные для формальных параметров base и n, а также локальная переменная р. Формальному параметру base присваивается фактическое значение dec, а формальному параметру n – фактическое значение i (base=dec, n=i). Формальный параметр n является переменной для счета числа шагов цикла, чтобы мы ни делали с n внутри функции power(), это не окажет никакого влияния на саму переменную i в вызывающей программе.
В следующей примере функция swap меняет местами переменные в локальной области, переменные во внешней вызывающей программе остаются неизменными.
Пример 8.
…
//определение функции swap
void swap (int x, int y)
{int t=x;
x=y;
y=t;
printf (“swap: x= %d y=%d”, x, y);
}
void main()
{int a=3, b=7;
swap (a, b);
printf (“main: a= %d b=%d”, a, b);
}
Результат выполнения программы
swap: x=7 y=3
main: a=3 b=7
Итак, при передаче параметров по значению переменные в вызывающей программе недоступны для функции. Если же требуется изменить переменную в вызывающей программе, следует передавать параметры через указатель на переменную.