Вызов по значению и вызов по ссылке.
В предыдущих примерах аргументы передаются функциям по значению. Когда переменные передаются по значению, в функцию передается копия текущего значения этой переменной. Поскольку передается копия переменной, сама эта переменная внутри вызывающей функции не изменяется. Вызов по значению — наиболее распространенный способ передачи информации (параметров) в функцию, и этот метод в С и C++ задан по умолчанию. Главным ограничением вызова по значению является то, что функция обычно возвращает только одно значение.
При вызове по ссылке в функцию передается не текущее значение, а адрес аргумента. Программе требуется меньше памяти, чем при вызове по значению. Кроме того, переменные в вызывающей функции могут быть изменены. Еще одним достоинством этого метода является то, что функция может возвращать более одного значения.
В результате выполнения операции & определяется адрес ячейки памяти, которая соответствует переменной. Если pooh — имя переменной, то &рooh — ее адрес. Можно представить себе адрес как ячейку памяти, но можно рассматривать его и как метку, которая используется компьютером для идентификации переменной. Предположим, мы имеем оператор
pooh = 24;
Пусть также адрес ячейки, где размещается переменная pooh, — 12126. Тогда в результате выполнения оператора
printf( %d %d\n" , pooh, &pooh);
получим
24 12126
Более того, машинный код, соответствующий первому оператору, словами можно выразить приблизительно так: «Поместить число 24 в ячейку с адресом 12126».
Воспользуемся указанной выше операцией для проверки того, в каких ячейках хранятся значения переменных, принадлежащих разным функциям, но имеющих одно и то же имя.
/* контроль адресов */
#include<stdio.h>
void mikado(int);
void main()
{
int pooh = 2, bah = 5;
printf(" B main(), pooh = %d u &pooh = %u\n" , pooh, &pooh);
printf(" B main(), bah = %d u &bah = %u\n", bah, &bah);
mikado(pooh);
}
void mikado(int bah)
{
int pooh = 10;
printf(" B mikado(), pooh = %d u &pooh = %u\n", pooh, &pooh);
printf(" B mikado(), bah = %d u &bah = %u\n" , bah, &bah);
}
Мы воспользовались форматом %u (целое без знака) для вывода на печать адресов на тот случай, если их величины превысят максимально возможное значение числа типа int. В нашей вычислительной системе результат работы этой маленькой программы выглядит так:
В main(), pooh = 2 и &pooh = 56002
В main(), bah = 5 и &bah = 50004
В mikado(), pooh = 10 и &pooh = 55994
В mikado(), bah = 2 и &bah = 56000
Использование указателей для связи между функциями
Мы только прикоснулись к обширному и увлекательному миру указателей. Сейчас нашей целью является использование указателей для решения задачи об установлении связи между функциями. Ниже приводится программа, в которой указатели служат средством, обеспечивающим правильную работу функции, которая осуществляет обмен значениями переменных.
#include<stdio.h>
void interchange(int * ,int * );
void main()
{
int x = 5, y = 10;
printf(" At the beginning x = %d and y = %d.\n" , x, y);
interchange(&x,&y); /* передача адресов функции */
printf(" And now x = %d and y = %d.\n" , x, y);
}
void interchange(int *u, int *v) /* u и v являются указателями */
{
int temp;
temp = *u; /* temp присваивается значение, на которое указывает u */
*u = *v;
*v = temp;
}
После всех встретившихся трудностей, проверим, работает ли этот вариант!
Посмотрим, как она работает. Во-первых, теперь вызов функции выглядит следующим образом:
interchange(&x, &y);
Вместо передачи значений х и у мы передаем их адреса. Это означает, что формальные аргументы и и v, имеющиеся в спецификации
interchange(u,v)
при обращении будут заменены адресами и, следовательно, они должны быть описаны как указатели. Поскольку х и у — целого типа, а u и v являются указателями на переменные целого типа, и мы вводим следующее описание:
int *u, *v;
Далее в теле функции оператор описания
int temp
используется с целью резервирования памяти. Мы хотим поместить значение переменной х в переменную temp, поэтому пишем
temp = *u;
Вспомните, что значение переменной u — это &х, поэтому переменная и ссылается на х. Это означает, что операция *u дает значение х, которое как раз нам и требуется. Мы не должны писать, например, так:
temp = u; /* неправильно */
поскольку при этом происходит запоминание адреса переменной х, а не ее значения; мы же пытаемся осуществить обмен значениями, а не адресами.
Точно так же, желая присвоить переменной у значение переменной х, мы пользуемся оператором
*u = *v;
который соответствует оператору
х = у;
Подведем итоги. Нам требовалась функция, которая могла бы изменять значения переменных x и у. Путем передачи функции адресов переменных х и у мы предоставили ей возможность доступна к ним. Используя указатели и операцию *, функция смогла извлечь величины, помещенные в соответствующие ячейки памяти, и поменять их местами.
Вообще говоря, при вызове функции информация о переменной может передаваться функции в двух видах. Если мы используем форму обращения
function1(х);
происходит передача значения переменной х. Если же мы используем форму обращения
function2(&x);
происходит передача адреса переменной х. Первая форма обращения требует, чтобы определение функции включало в себя формальный аргумент того же типа, что и х:
function1(num)
int num;
Вторая форма обращения требует, чтобы определение функции включало в себя формальный аргумент, являющийся указателем на объект соответствующего типа:
function2(ptr)
int *ptr;
80. Использование функций. Рекурсия. Равноправность функций в языке Си. Параметры и аргументы функций. Формальные и фактические параметры.
Функция — это самостоятельная единица программы, которая спроектирована для реализации конкретной подзадачи.
Функция является подпрограммой, которая может содержаться в основной программе, а может быть создана отдельно (в библиотеке). Каждая функция выполняет в программе определенные действия.
Сигнатура функции определяет правила использования функции. Обычно сигнатура представляет собой описание функции, включающее имя функции, перечень формальных параметров с их типами и тип возвращаемого значения.
Семантика функции определяет способ реализации функции. Обычно представляет собой тело функции.