6.5. СТРУКТУРА ПРОГРАММЫ НА СИ. ПОНЯТИЕ О ФУНКЦИЯХ

К оглавлению1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 

 

Программа на языке Си представляет собой набор последовательно описанных функций (процедуры и подпрограммы в языке Си считаются частным случаем функций). Каждая функция - самостоятельная единица программы, предназначенная для решения определенной задачи (или подзадачи). При описании она имеет следующий вид:

 

Тип_функцни Имя (<список аргументов>)

<описания аргументов>

{

<описания>

<операторы>

}

 

Отметим, что список аргументов может быть пустым (однако, скобки после имени функции сохраняются). В этом случае, естественно, нет и их описаний.

Имеется одна главная функция (с именем main), с которой начинается выполнение программы. Функции могут обращаться к другим функциям посредством конструкций вызова. Вызов функции используется при вычислении значения выражения. В результате вызова функция возвращает вычисленное значение, которое и является значением вызова функции. Попутно функция может преобразовывать значения своих аргументов. Такой результат вызова функции называется побочным эффектом.

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

Пример. Пусть необходимо вывести на экран словосочетание «Простая функция без аргументов» 15 раз, используя функции.

 

Программа 97

#include<stdio.h>

main ()

(

int i,print() ;

for (i=l;i<=15;i++) print();

{

print() /* вызываемая функция без аргументов */

)

printf ("Простая функция без аргументов\n»);

}

 

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

Следует различать формальные аргументы, используемые при описании функций, и фактические, задаваемые при вызове функций. Формальный аргумент - переменная в вызываемой программе, а фактический аргумент - конкретное значение, присвоенное этой переменной вызывающей программой. Фактический аргумент может быть константой, переменной или даже более сложным выражением. Независимо от типа фактического аргумента он вначале вычисляется, а затем его величина (в данном случае некоторое целое число) передается функции (см. программу 97). Можно задавать список аргументов, разделенных запятыми. Пример: программа 98, которая находит числа х, у, z, принадлежащие отрезку [1;20] и удовлетворяющие условию x^2 = у^2 + z^2.

 

Программа 98

#include<stdio.h>

main()

(

int х, у, z, zero() ;

char p, q, ch() ;

x=2; y=45; z=0;

q=’o’;

printf("х» "); zero(x);

printf("x+y+(x+y)^z= "); zero(x+y+(x+y)^z) ;

printf("q= "); ch(q);

}

int zero(u)

int u;

(

printf("%d\n",u);

)

char ch(u)

char u;

{

printf("%c\n",u);

)

Результат работы программы:

 

x=2

x+y+(x+y)^z= 94

q=0

 

Программа 99

#include<stdio.h>

main()

(

int x,y,z;

int zero();

printf("Следующие величины могут быть сторонами прямоугольного треугольника:\n");

for (х=1;х<=20;х++)

for (у=1;у<=20;у++)

for (z=l;z<=20;z++)

if (y*y+z*z==x*x)

zero(x,у,z);

}

int zero(f,g,h)

int f,g,h;

(

printf ("x= %d, y= %d, 2=%d\n",f,g,h);

)

Результат работы программы:

 

следующие величины могут быть сторонами прямоугольного треугольника

х= 5, у= 3, z= 4

х= 5, у= 4, z= 3 x= 10, y= 6, z= 8

x= 10, y=8, z=6 x= 13, y=5, z= 12

x= 13, y= 12, z= 5 x= 15, y= 9, z= 12

x= 15, y= 12, z=9 x=17, y=8, z=15 x= 17, y= 15,

z=8 x=20, y=12, z=16

x=20, y= 16, z= 12

 

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

 

return (<выражение>);

 

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

Достижение «конца» функции (правой закрывающей фигурной скобки) эквивалентно выполнению оператора return без возвращаемого значения (т.е. оператор return в конце функции может быть опущен).

Пример. Данная программа вычисляет факториал, если число меньше 8; если вводимое число больше или равно 8, то выводится сообщение «Очень большое число».

 

Программа 100

#include<stdio.h>

main()

{

int n, s() ;

printf("Введите число ");

scant("%d", &n) ;

if (n<8) printf(=%d", s(n)) ;

else printf("Очень большое число");

)

int s(x) /* определение функции с параметром */

int x;

{

int y,p=l;

for (y=x; y>0; y- ) p*=y;

return(p); /* Возвращает в основную программу значение р */

}

Результат работы программы:

 

1. Введите число 4

р=24

2.Введите число 9

Очень большое число

 

Пример: предположим, что нужно вычислить x2 (для некоторого неотрицательного целого у) (очевидный способ реализации возведения в целочисленную степень -многократное умножение, но существует более эффективный алгоритм, приведенный ниже).

 

Программа 101

 

#include<stdio.h>

main()

(

int а, Ь, x, у, z;

int odd() ;

printf("\nВведите x, у через пробел: ");

scanf("%d %d", &х, &у); а=х; Ь=у; z=l;

while (b!=0)

if (odd(b))

{ z=z*a; b- -;}

else

( a=a*a; b=b/2;}

printf("\n%d", z);

}

int odd(t)

int t;

(

return((t%2==0)? 0:1);

)

Результат работы программы:

 

Введите x, у через пробел: 15 2

225

 

Если функции необходимо вернуть несколько значений, можно использовать два различных приема:

• применить глобальные переменные (в этом случае кроме изученных ранее характеристик переменных (имени, типа, значения), используется еще одна – класс памяти, см.ниже);

• применить переменные типа «указатель» в качестве аргументов функции. При вызове функции информация о переменной может передаваться функции в двух видах. Если мы используем форму обращения

 

function 1(х);

 

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

 

function2(&x);

 

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

 

function l(num)

int num;

 

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

 

function2(x)

int *x;

 

Обычно пользуются первой формой, если входное значение необходимо функции для некоторых вычислений или действий, и второй формой, если функция должна будет изменять значения переменных в вызывающей программе. Выше вторая форма вызова \же применялась при обращении к ф\нкции scanf(). Когда мы хотим ввести некоторое значение в переменную num, мы пишем scanf(''%d",&num). Данная функция читает значение, затем, используя адрес, который ей дается, помещает это значение в память.

 

Пример: пусть необходимо поменять местами заданные значения переменных х и у.

 

Программа 102

 

#include<stdio.h>

main()

{

int х, у;

int interchange(); /* описание функции типа int */

x=l; y=3;

printf("Имели... x=l y=3\n") ;

interchange(&x, &y); /* обращение к функции (в данном случае передаются адреса переменных) */

printf("Получили... x=%d y=%d", х, у);

}

/* вызываемая функция */

int interchange(u, v)

int *u, *v;

(

int p;

p=*\i; *u=*v; *v=p;

}

Результат работы программы:

 

Имели х=1 у=3

Получили х=3 у=1

 

В этой программе в вызове функции interchange(&x,&y) вместо передачи значений х и у мы передаем их адреса. Это означает, что формальные аргументы и и v, имеющиеся в спецификации interchanage(u,v), при обращении будут заменены адресами и, следовательно, они должны быть описаны как указатели.

Поскольку х и у целого типа, u и v являются указателями на переменные целого типа, и мы вводим следующее описание:

 

int *u,*v; int p;

 

Оператор описания используется с целью резервирования памяти. Мы хотим поместить значение переменной х в переменную р, поэтому пишем: р=*u; Вспомните, что значение переменной u - это &х, поэтому переменная и ссылается на х. Это означает, что операция *u дает значение х, которое как раз нам и требуется. Мы не должны писать, например, так:

 

р = u; /* неправильно */

 

поскольку при этом происходит запоминание адреса переменной х, а не ее значения. Аналогично, оператор *u = *v соответствует оператору х = у.

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

1. Описать тип функции в ее определении:

 

char pun(ch,n) /* функция возвращает символ */

int n;

char ch;

 

2. Описать тип функции также в вызывающей программе. Описание функции должно быть проведено наряду с описаниями переменных программы; необходимо только указать скобки (но не аргументы) для идентификации данного объекта как функции.

 

main()

{

char rch,pun();

 

 

Программа на языке Си представляет собой набор последовательно описанных функций (процедуры и подпрограммы в языке Си считаются частным случаем функций). Каждая функция - самостоятельная единица программы, предназначенная для решения определенной задачи (или подзадачи). При описании она имеет следующий вид:

 

Тип_функцни Имя (<список аргументов>)

<описания аргументов>

{

<описания>

<операторы>

}

 

Отметим, что список аргументов может быть пустым (однако, скобки после имени функции сохраняются). В этом случае, естественно, нет и их описаний.

Имеется одна главная функция (с именем main), с которой начинается выполнение программы. Функции могут обращаться к другим функциям посредством конструкций вызова. Вызов функции используется при вычислении значения выражения. В результате вызова функция возвращает вычисленное значение, которое и является значением вызова функции. Попутно функция может преобразовывать значения своих аргументов. Такой результат вызова функции называется побочным эффектом.

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

Пример. Пусть необходимо вывести на экран словосочетание «Простая функция без аргументов» 15 раз, используя функции.

 

Программа 97

#include<stdio.h>

main ()

(

int i,print() ;

for (i=l;i<=15;i++) print();

{

print() /* вызываемая функция без аргументов */

)

printf ("Простая функция без аргументов\n»);

}

 

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

Следует различать формальные аргументы, используемые при описании функций, и фактические, задаваемые при вызове функций. Формальный аргумент - переменная в вызываемой программе, а фактический аргумент - конкретное значение, присвоенное этой переменной вызывающей программой. Фактический аргумент может быть константой, переменной или даже более сложным выражением. Независимо от типа фактического аргумента он вначале вычисляется, а затем его величина (в данном случае некоторое целое число) передается функции (см. программу 97). Можно задавать список аргументов, разделенных запятыми. Пример: программа 98, которая находит числа х, у, z, принадлежащие отрезку [1;20] и удовлетворяющие условию x^2 = у^2 + z^2.

 

Программа 98

#include<stdio.h>

main()

(

int х, у, z, zero() ;

char p, q, ch() ;

x=2; y=45; z=0;

q=’o’;

printf("х» "); zero(x);

printf("x+y+(x+y)^z= "); zero(x+y+(x+y)^z) ;

printf("q= "); ch(q);

}

int zero(u)

int u;

(

printf("%d\n",u);

)

char ch(u)

char u;

{

printf("%c\n",u);

)

Результат работы программы:

 

x=2

x+y+(x+y)^z= 94

q=0

 

Программа 99

#include<stdio.h>

main()

(

int x,y,z;

int zero();

printf("Следующие величины могут быть сторонами прямоугольного треугольника:\n");

for (х=1;х<=20;х++)

for (у=1;у<=20;у++)

for (z=l;z<=20;z++)

if (y*y+z*z==x*x)

zero(x,у,z);

}

int zero(f,g,h)

int f,g,h;

(

printf ("x= %d, y= %d, 2=%d\n",f,g,h);

)

Результат работы программы:

 

следующие величины могут быть сторонами прямоугольного треугольника

х= 5, у= 3, z= 4

х= 5, у= 4, z= 3 x= 10, y= 6, z= 8

x= 10, y=8, z=6 x= 13, y=5, z= 12

x= 13, y= 12, z= 5 x= 15, y= 9, z= 12

x= 15, y= 12, z=9 x=17, y=8, z=15 x= 17, y= 15,

z=8 x=20, y=12, z=16

x=20, y= 16, z= 12

 

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

 

return (<выражение>);

 

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

Достижение «конца» функции (правой закрывающей фигурной скобки) эквивалентно выполнению оператора return без возвращаемого значения (т.е. оператор return в конце функции может быть опущен).

Пример. Данная программа вычисляет факториал, если число меньше 8; если вводимое число больше или равно 8, то выводится сообщение «Очень большое число».

 

Программа 100

#include<stdio.h>

main()

{

int n, s() ;

printf("Введите число ");

scant("%d", &n) ;

if (n<8) printf(=%d", s(n)) ;

else printf("Очень большое число");

)

int s(x) /* определение функции с параметром */

int x;

{

int y,p=l;

for (y=x; y>0; y- ) p*=y;

return(p); /* Возвращает в основную программу значение р */

}

Результат работы программы:

 

1. Введите число 4

р=24

2.Введите число 9

Очень большое число

 

Пример: предположим, что нужно вычислить x2 (для некоторого неотрицательного целого у) (очевидный способ реализации возведения в целочисленную степень -многократное умножение, но существует более эффективный алгоритм, приведенный ниже).

 

Программа 101

 

#include<stdio.h>

main()

(

int а, Ь, x, у, z;

int odd() ;

printf("\nВведите x, у через пробел: ");

scanf("%d %d", &х, &у); а=х; Ь=у; z=l;

while (b!=0)

if (odd(b))

{ z=z*a; b- -;}

else

( a=a*a; b=b/2;}

printf("\n%d", z);

}

int odd(t)

int t;

(

return((t%2==0)? 0:1);

)

Результат работы программы:

 

Введите x, у через пробел: 15 2

225

 

Если функции необходимо вернуть несколько значений, можно использовать два различных приема:

• применить глобальные переменные (в этом случае кроме изученных ранее характеристик переменных (имени, типа, значения), используется еще одна – класс памяти, см.ниже);

• применить переменные типа «указатель» в качестве аргументов функции. При вызове функции информация о переменной может передаваться функции в двух видах. Если мы используем форму обращения

 

function 1(х);

 

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

 

function2(&x);

 

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

 

function l(num)

int num;

 

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

 

function2(x)

int *x;

 

Обычно пользуются первой формой, если входное значение необходимо функции для некоторых вычислений или действий, и второй формой, если функция должна будет изменять значения переменных в вызывающей программе. Выше вторая форма вызова \же применялась при обращении к ф\нкции scanf(). Когда мы хотим ввести некоторое значение в переменную num, мы пишем scanf(''%d",&num). Данная функция читает значение, затем, используя адрес, который ей дается, помещает это значение в память.

 

Пример: пусть необходимо поменять местами заданные значения переменных х и у.

 

Программа 102

 

#include<stdio.h>

main()

{

int х, у;

int interchange(); /* описание функции типа int */

x=l; y=3;

printf("Имели... x=l y=3\n") ;

interchange(&x, &y); /* обращение к функции (в данном случае передаются адреса переменных) */

printf("Получили... x=%d y=%d", х, у);

}

/* вызываемая функция */

int interchange(u, v)

int *u, *v;

(

int p;

p=*\i; *u=*v; *v=p;

}

Результат работы программы:

 

Имели х=1 у=3

Получили х=3 у=1

 

В этой программе в вызове функции interchange(&x,&y) вместо передачи значений х и у мы передаем их адреса. Это означает, что формальные аргументы и и v, имеющиеся в спецификации interchanage(u,v), при обращении будут заменены адресами и, следовательно, они должны быть описаны как указатели.

Поскольку х и у целого типа, u и v являются указателями на переменные целого типа, и мы вводим следующее описание:

 

int *u,*v; int p;

 

Оператор описания используется с целью резервирования памяти. Мы хотим поместить значение переменной х в переменную р, поэтому пишем: р=*u; Вспомните, что значение переменной u - это &х, поэтому переменная и ссылается на х. Это означает, что операция *u дает значение х, которое как раз нам и требуется. Мы не должны писать, например, так:

 

р = u; /* неправильно */

 

поскольку при этом происходит запоминание адреса переменной х, а не ее значения. Аналогично, оператор *u = *v соответствует оператору х = у.

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

1. Описать тип функции в ее определении:

 

char pun(ch,n) /* функция возвращает символ */

int n;

char ch;

 

2. Описать тип функции также в вызывающей программе. Описание функции должно быть проведено наряду с описаниями переменных программы; необходимо только указать скобки (но не аргументы) для идентификации данного объекта как функции.

 

main()

{

char rch,pun();