Информатика -продвинутый курс


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


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

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

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

{

<описания>

<операторы>

}

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

Имеется одна главная функция (с именем 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();


Содержание раздела