Вызов по значению и вызов по ссылке.

В предыдущих примерах аргументы передаются функциям по значению. Когда переменные передаются по значению, в функцию передается копия текущего значения этой переменной. Поскольку передается копия переменной, сама эта переменная внутри вызывающей функции не изменяется. Вызов по значению — наиболее распространенный способ передачи информации (параметров) в функцию, и этот метод в С и 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. Использование функций. Рекурсия. Равноправность функций в языке Си. Параметры и аргументы функций. Формальные и фактические параметры.

 

Функция — это самостоятельная единица программы, которая спроектирована для реализации конкретной подзадачи.
Функция является подпрограммой, которая может содержаться в основной программе, а может быть создана отдельно (в библиотеке). Каждая функция выполняет в программе определенные действия.

Сигнатура функции определяет правила использования функции. Обычно сигнатура представляет собой описание функции, включающее имя функции, перечень формальных параметров с их типами и тип возвращаемого значения.

Семантика функции определяет способ реализации функции. Обычно представляет собой тело функции.