Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Учебное пособие 300070.doc
Скачиваний:
6
Добавлен:
30.04.2022
Размер:
295.42 Кб
Скачать

3.Подпрограмма

Синтаксически подпрограмма состоит из:

– определения подпрограммы,

– обращения к подпрограмме.

Определение подпрограммы:

<определение подпрограммы> ::= <заголовок подпрограммы> <тело подпрограммы>

Тело подпрограммы является описанием того частичного алгоритма, который объявляется подпрограммой.

Заголовок подпрограммы задает ее имя и определяет данные, с которыми эта подпрограмма работает.

Обращение к подпрограмме служит для ее активации.

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

4.Функции языка Си

Программа на языке Си это совокупность подпрограмм – функций (в дальнейшем просто функций), среди которых выделяется одна обязательная (главная) с именем main. Остальные функции вводятся в структуру программы для улучшения ее структурированности. Их выполнение инициируется прямо или косвенно вызовами из функции main.

В простом случае определение функции на языке Си:

<Определение функции> ::= <заголовок функции> <блок>

<заголовок функции> <составной оператор>

<Заголовок функции> :: =

<cпецификация типа результата функции> <имя функции> ( [ <cписок формальных параметров> ] )

| void <имя функции> ( [ <cписок формальных параметров> ] )

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

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

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

Обращение к функции, возвращающей результат, это выражение вида:

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

или <имя функции> ( ) , если у функции нет формальных параметров. Вызов такой функции является операндом какого-либо выражения.

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

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

или <имя функции> ( );

Одним из важных моментов при организации подпрограмм является задание данных, над которыми будут выполняться действия подпрограммы. Данные в подпрограмму можно передавать двумя способами: 1) используя для этого глобальные объекты, 2) используя формальные параметры.

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

Пример определения функции без параметров:

int MAX ( )

{ return ( a > b ? a : b ) ; }

Функция MAX может выполнять действия только над переменными a и b (входные данные функции). Переменные a и b должны быть определены вне функций и называются внешними или глобальными переменными. Тело функции MAX – составной оператор. Обращение к функции MAX – операнд выражения, например : x = MAX ( ); Переменной x будет присвоено значение функции MAX, т.е. наибольшее из значений a и b.

Чтобы сделать подпрограмму более гибкой и обеспечить ее общность, данные в подпрограмму передаются через список формальных параметров. Формальные параметры задают не какие-то конкретные данные программы, а лишь условно определяют объекты подпрограммы. Это позволяет применять подпрограммы к различным объектам программы, конкретизируя при каждом обращении ее данные в списке фактических параметров. Все объекты подпрограммы можно разделить на входные данные, промежуточные и выходные. В список формальных параметров необходимо вводить только те объекты, которые изменяются при вызове подпрограммы, т.е. входные и выходные данные.

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

<спецификация типа> <имя параметра>

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

Связь между фактическими и формальными параметрами может устанавливаться двумя способами:

– связь по значению – значение фактического параметра присваивается формальному. Действия в подпрограмме выполняются над формальной переменной, получившей значение фактического параметра. Такой способ используется, прежде всего, для передачи входных данных в подпрограмму;

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

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

Пример определения функции с параметрами:

int MAX (int a, int b)

{ return a > b ? a : b ; }

Функция MAX выполняет действия над формальными переменными a и b (входные данные функции). Переменные a и b получают значения фактических параметров при обращении к MAX (т.е. связь между параметрами устанавливается по значению). Результат функции возвращается в точку вызова при выполнении оператора return. Обращение к функции MAX – операнд выражения, например : x = MAX (n+1, m*m ); Переменной x будет присвоено значение функции MAX, т.е. наибольшее из значений выражений n+1 и m*m.

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

Пример определения функции с входными и выходными параметрами:

void MAX (int a, int b, int *c)

{ *c = a > b ? a : b ; }

Функция MAX выполняет действия над формальными переменными a , b и с. Переменные a , b и c получают значения фактических параметров при обращении к MAX (т.е. связь между параметрами устанавливается по значению). Результат функции передается через формальный параметр типа указатель - с. Фактический параметр, соответствующий с – это адрес переменной, которой надо присвоить результат. При выполнении оператора присваивания, в левой части которого стоит выражение *c, происходит обращение к области памяти (к переменной), адрес которой является значением с. И туда помещается вычисленное значение правой части оператора присваивания. Обращение к функции MAX – оператор вызова функции, например : MAX ( n+1, m*m, y); Переменная y получает значение наибольшего из выражений n+1 и m*m.

Связь по адресу в Си используется только для передачи в функцию параметра типа массив и для передачи в качестве параметра функции.

Пример1. Программа с функцией, параметры которой типа массив

# include <stdio.h>

# define n 15

# define n1 10

# define n2 20

void MULT (float a[ ][n1], float b[ ][n2], float[ ][n2], int n, int m, int p)

{ int i, j, k ;

float s;

for( i=0; i < n; i++ )

for( j=0; j < p; j++ )

{ for( s=k=0; k < m; k++ )

s += a[i][k] * b[k][j];

c[i][j] = s; }

}

void main ( )

{ float x[n] [n1], y[n1] [n2], z [n] [n2];

int i, j, k, str, stb1, stb2 ;

printf (“\n введи размерность матриц \n”);

scanf (“%d%d%d”, &str, &stb1,&stb2);

printf (“\n введи первую матрицу \n”);

for( i=0; i < str; i++ )

for( j=0; j < stb1; j++ ) scanf (“%f ”, &x[i][j] );

printf (“\n введи вторую матрицу \n”);

for( i=0; i < stb1; i++ )

for( j=0; j < stb2; j++ ) scanf (“%f ”, &y[i][j] );

MULT(x, y, z, str, stb1, stb2);

printf (“\n произведение матрицы x на матрицу y \n”);

for( i=0; i < str; i++ )

{ for( j=0; j < stb2; j++ ) printf (“%f ”, z[i][j] );

printf (“\n”); }

}

Здесь a, b и c – формальные параметры типа массив функции MULT. Они являются входными данными функции (массивы a и b) и выходными (массив c). Формальные параметры целого типа n, m, p – входные данные функции MULT – размерности матриц. При обращении к MULT из главной функции формальному параметру a соответствует фактический массив x, формальному b соответствует фактический y, параметру c – фактический массив z. Связь между параметрами устанавливается по адресу. Это значит, что формальному параметру a присваивается адрес фактического массива x, формальному b присваивается адрес фактического массива y, а формальному c адрес фактического z. Таким образом, формальные параметры a, b, c на самом деле параметры типа указатель на объекты типа float. Поэтому размерность формального массива по первому измерению можно опускать (остальные размерности не опускаются, т.к. они необходимы для вычисления адреса элемента массива). При выполнении действий над этими параметрами из них извлекается адрес фактического массива и, действия выполняются над элементами фактического массива. Таким образом, при присваивании c[ i,j] значения переменной s происходит обращение к элементу z[ i,j] фактического массива, который и получает значение s.

Формальным параметрам n, m, p ставятся в соответствие фактические str, stb1, stb2. Связь между этими параметрами устанавливается по значению. Это значит, что формальному параметру n присваивается значение фактической переменной str, формальной m присваивается значение фактической переменной stb1, а формальной p значение фактической stb2. Таким образом, в функции действия выполняются над формальными переменными n, m, p, получившими значения фактических переменных.

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

Пример 2. Функция с параметрами и возвращаемым результатом типа указатель

int *max_vect ( int n, int *x, int *y )

{ static int z [nmax ], i;

for (i=0; i < n; i++)

z [i] = x[ i] > y[ i] ? x[ i] : y[ i];

return ( &z ); }

Здесь nmax именованная внешняя константа, а возвращаемый результат - адрес локального массива z функции max_vect. Входные параметры x, y – указатели на величины типа int. Обратиться к функции можно, например, из оператора p = max_vect (10, a, b); или p = max_vect ( 10, &a[0], &b[0]). В первом случае a и b имена фактических массивов и связь между формальными параметрами x и y и фактическими массивами a и b устанавливается по адресу. Во втором случае все фактические параметры – выражения, способ связи между формальными и фактическими параметрами – по значению. Переменная p – это переменная типа указатель на величину типа int.

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

<cпецификация типа результата функции> <имя функции> ( [ <cписок формальных параметров> ] )

| void <имя функции> ( [ <cписок формальных параметров> ] )

Основные отличия: 1) в конце прототипа ставится точка с запятой, 2) имена формальных параметров можно опускать.

Пример 3. Программа с определением и прототипом функций

# include <stdio.h>

int MAX (int a, int b) //Определение до вызова функции

{ return a > b ? a : b ; }

void main ( )

{ float cube ( float x); //Прототип до определения и вызова функции

int Max, p = 2; scanf (“%d ”, &Max );

Max = MAX (( int) cube ( (float) p), Max); //Явные преобразования типов фактических параметров

printf (“\n наибольшее равно %d ”, Max );

}

float cube ( float x) //Определение функции

{ return x*x*x ; }

Преобразования типов фактических параметров при обращении к функции cube - (float) p и функции MAX (первый параметр) требуются для согласования типов формальных и фактических параметров. Прототип функции cube необходим, так как ее определение размещено после обращения к ней.