СТРУКТУРА ПРОГРАММЫ НА СИ. ПОНЯТИЕ О ФУНКЦИЯХ
Программа на языке Си представляет собой набор последовательно описанных функций (процедуры и подпрограммы в языке Си считаются частным случаем функций). Каждая функция - самостоятельная единица программы, предназначенная для решения определенной задачи (или подзадачи). При описании она имеет следующий вид:
Тип_функцни Имя (<список аргументов>)
<описания аргументов>
{
<описания>
<операторы>
}
Отметим, что список аргументов может быть пустым (однако, скобки после имени функции сохраняются). В этом случае, естественно, нет и их описаний.
Имеется одна главная функция (с именем 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();