8.8. Функции

 

Функции разбивают большие вычислительные задачи на маленькие подзадачи и позволяют использовать в работе то, что уже сделано другими, а не начинать каждый раз с пустого места.

Каждая функция имеет вид

Тип-результата имя-функции (список формальных параметров)

описания формальных параметров

 {

     описания и операторы

 }

Некоторые части могут отсутствовать; минимальной функцией является

DUMMY ()  { }

которая не совершает никаких действий.

Если тип результата не задан, то по умолчанию функция имеет тип-результата int.

Выполнение функции завершается, если выполнено тело функции или оператор return. Оператор return служит механизмом для возвращения значения из вызванной функции в функцию, которая к ней обратилась. За return может следовать любое выражение:

return выражение;

Тип выражения должен совпадать с типом-результатом или должен иметь тип, который может быть приведен к типу-результату. Более того, после return может не быть вообще никакого выражения; в этом случае в вызывающую программу не передается никакого значения. Управление также возвращается в вызывающую программу без передачи какого-либо значения и в том случае, когда при выполнении мы выполним все тело  функции, достигая закрывающейся правой фигурной скобки.

Если мы хотим, чтобы функция не возвращала никакого значения, нужно ее тип-результат сделать void.

Описания формальных параметров может находиться в списке формальных параметров.

Например

Функция sum, вычисляющая сумму трех вещественных чисел

1.

Floatsum(a,b,c) /*список формальных параметров*/

Floata,b,c; /*описания формальных параметров*/

{

         returna+b+c;  /*оператор return возвращает результат работы функции в вызывающую программу*/

}

 

2.

floatsum(floata, floatb, floatc)/*описание параметров в списке параметров*/

{

         return a+b+c; 

}

 

Вызовфункции

 

Вызов функции имеет следующий вид:

Имя-функции(список фактических параметров);

Где фактические параметры могут быть выражениями.

Пример

int max(int a,int b)

{ if(a>b) return a;

   elsereturnb;

}

 

вызывать эту функцию можно

int i=7;

i=max(i,8);

i=max(max(i,9),4);

 

Передача параметров обычно реализуется двумя способами: передачей по значению и передачей по ссылке. При передаче по значению значения фактических параметров копируются в формальные параметры. При передаче параметров по ссылке формальные параметры фактически становятся синонимами для фактических параметров.

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

В языке Си передача параметров происходит только по значению.

Исключение является передача функции имени массива,  если в качестве аргумента функции выступает имя массива, то передается адрес начала этого массива; сами элементы не копируются. Функция может изменять элементы массива, используя индексацию и адрес начала. Таким образом, массив передается по ссылке.

 

Пример

Функция выдает длину строки

int strlen(char * p)

{

         int n=0;

         while(*p++!=’\0’) n++;

         return n;

}

 

void main()

{ char string[]=”hello”;

   printf(“%d”, strlen(string));/*напечатает количество символов в строке 5*/

}

 

Операция увеличения p совершенно законна, поскольку эта переменная является указателем; p++ никак не влияет на символьную строку в обратившейся к strlen функции, а только увеличивает локальную для функции strlen копию адреса. Описания формальных параметров в определении функции в виде

 

char s[];

char *s;

 

совершенно эквивалентны; какой вид описания следует предпочесть, определяется в значительной степени тем, какие выражения будут использованы при написании функции. Если функции передается имя массива, то в зависимости от того, что удобнее, можно полагать, что функция оперирует либо с массивом, либо с указателем, и действовать далее соответвующим образом. Можно даже использовать оба вида операций, если это кажется уместным и ясным.

    Можно передать функции часть массива, если задать в качестве аргумента указатель начала подмассива. Например, если

A - массив, то как

 

F(&A[2])

 

как и

 

F(A+2)

передают  функции F адрес элемента A[2], потому что и &A[2], и A+2 являются указательными  выражениями,  ссылающимися  на третий элемент A. внутри функции F описания аргументов могут присутствовать в виде:

 

F(ARR)

int ARR[];

{

...

}

 

или

 

F(ARR)

int *ARR;

{

...

}

 

Что касается функции F, то тот факт, что ее аргумент в действительности ссылается к части большего массива, не имеет для нее никаких последствий.

Что же делать, если нам действительно надо изменить фактический параметр? например, программа сортировки захотела бы поменять два нарушающих порядок элемента с помощью функции с именем SWAP. Для этого недостаточно написать

 

SWAP(A, B);

 

определив функцию SWAP при этом следующим образом:

 

SWAP(X, Y)      /* WRONG */

int X, Y;

{

int TEMP;

TEMP = X;

X = Y;

Y = TEMP;

}

 

из-за  вызова  по  значению  SWAP не может воздействовать на агументы A и B в вызывающей функции.

К счастью, все же имеется возможность получить  желаемый эффект.  Вызывающая  программа передает указатели подлежащих изменению значений:

SWAP(&A, &B);

так как операция & выдает адрес переменной, то  &A  является указателем на A. В самой SWAP формальные параметры записываются как указатели и доступ к фактическим операндам осуществляется через них.

SWAP(PX, PY)    /* INTERCHANGE *PX AND *PY */

int *PX, *PY;

{

int TEMP;

TEMP = *PX;

*PX = *PY;

*PY = TEMP;

}

 

К оглавлению

Назад к разделу "8.7. Указатели"

Вперед к разделу "8.9. Место определения и обращения к функции"