Программирование подобного класса задач опирается на структуры предыдущего раздела, с учетом особенностей, связанных с использованием входных и выходных параметров.
Каждая из функций Си представляется одной из упрощенных структур
[тип] имя([тип b1,. . .,тип bi,. . .,тип bn,тип *d1,. . .,тип *dj,. . .,тип *dm])
{
тело
функции
[return РВ;]
}
где имя – идентификатор (название) функции;
тип – описатель типа функции (результата);
bi – список входных формальных параметров с указанием типа каждого;
dj – список выходных формальных параметров – указателей, определяющих возвращаемые результаты, с указанием типа каждого и признака указателя при описании (*);
( ) – ограничители списка формальных параметров;
тело функции – основная часть (совокупность операторов), реализующая вынесенные в отдельный алгоритм вычисления (действия);
return РВ; – оператор возврата (return) результата вычислений;
РВ – результат вычислений (выражение);
[ ] – признак необязательности содержимого;
{ } – ограничители тела функции.
Первую строка структуры является заголовком функции.
Структура вызова функции:
имя ([a1, . . . , ai, . . . , an,c1, . . . , cj, . . . , cm])[;]
где имя – идентификатор функции;
a1, ai, an – список входных фактических параметров (аргументов), численные значения которых требуется передать в дополнительную функцию (подпрограмму);
c1, cj, cm – список адресов выходных фактических параметров (аргументов), численные значения которых требуется получить из дополнительной функции (подпрограммы);
( ) – ограничители аргументов;
[ ] – признак необязательности содержимого.
Прототип функции аналогичен ее заголовку и имеет структуру
[тип] имя([тип b1,. . .,тип bi,. . .,тип bn,тип *d1,. . .,тип *dj,. . .,тип *dm]);
где имя – идентификатор (название) функции;
тип – описатель типа функции (результата);
bi – список входных формальных параметров с указанием типа каждого;
dj – список выходных формальных параметров – указателей, определяющих возвращаемые результаты, с указанием типа каждого и признака указателя при описании (*);
( ) – ограничители списка формальных параметров;
[ ] – признак необязательности содержимого;
; – признак оператора.
Правила записи и использования функций с несколькими возвращаемыми значениями совпадают с указанными ранее для функций с одним результатом со следующими дополнениями:
7. количества и типы входных и выходных формальных параметров должны соответствовать аналогичным фактическим, т.е. каждый ai имеет свой bi, а каждый cj – свой dj, при этом тип и взаимное расположение их в списке определяется программистом;
8. в качестве входных формальных параметров используются переменные;
9. в качестве выходных формальных параметров используются указатели;
10.в качестве входных фактических параметров используются константы, переменные, вызовы функций, выражения и адреса массивов;
11.в качестве выходных фактических параметров используются адреса переменных и адреса массивов;
12.каждое обращение к подпрограмме, как правило, оформляется как отдельный оператор, в соответствии с требованиями алгоритма;
13.использование выходных параметров освобождает от ограничения на возвращение только одного результата оператором return, основной результат рекомендуется возвращать оператором return, все остальные в качестве выходных параметров.
14.при использовании структуры с оператором return, вызов функции может служить операндом выражения в вызываемой функции;
15.если логика задачи не позволяет выделить основной результат, то использование оператора return необязательно. В этом случае в заголовке дополнительной функции в качестве описателя типа необходимо указывать ключевое слово void;
16.вызов функции, оформленный простым оператором, заканчивается символом «;», если является операндом выражения – признак оператора не указывается;
17.формальные значения (кроме главного) возвращаются в ячейки вызывающей функции, адреса которых были переданы в вызываемую;
18.в списке формальных параметров прототипа допускается опускать их имена, оставляя для входных их типы, а для выходных типы с последующими символами «*».
Программная реализация задач с использованием входных и выходных параметров опирается на закономерности, описанные при алгоритмизации. В качестве выходных параметров используются адреса операндов и указатели на них.
Обращение по адресу определяет, что в дополнительной функции используется в качестве формального фактический параметр головной функции. Поэтому любые корректировки формального параметра в дополнительной функции есть автоматическое изменение фактического параметра в вызывающей функции. Принципиальные различия в обработке формальных параметров, заданных переменными или указателями, иллюстрируются схемами рис. 6.6, 6.7.
![](/img/baza/Lektsii-po-programmirovaniyu--2-.files/image268.gif)
Рис. 6.6. Передача параметра по значению
![](/img/baza/Lektsii-po-programmirovaniyu--2-.files/image270.gif)
Рис. 6.7. Передача параметра по адресу
Первая (передача по значению) показывает параллельное существование отдельных ячеек хранения формального и фактического параметра (переменной). Вторая (передача по адресу) определяет существование единой ячейки для хранения фактического параметра, обрабатываемого в дополнительной функции как формальный с использованием адреса.
Передача по значению позволяет передавать простые переменные в качестве входных параметров. В этом случае используются две различные ячейки (в вызывающей и вызываемой функции) для хранения значений локальных переменных с разными или одинаковыми именами.
Передача по адресу позволяет работать с переменными в качестве выходных (формальных и фактических) параметров. В этом случае для хранения их значений используются единые ячейки, выделяемые и описываемые в вызывающей функции, с разными при желании именами.
Аналогично передача по адресу позволяет работать с массивами в качестве входных и выходных параметров. В этом случае в вызывающей функции описывается массив (выделяются ячейки для хранения его элементов), используемый как фактический в основной (вызывающей) и формальный в дополнительной функции. При этом доступ к единому массиву из основной и дополнительной функции возможен под разными именами.
Достоинство передачи по адресу – возможность возвращения в вызывающую функцию значения выходного формального параметра.
Недостаток – любые изменения входного формального параметра есть автоматическое изменение аналогичного фактического, что, как правило, нежелательно (исходные значения входных фактических параметров в процессе счета изменять не рекомендуется).
. . .
main( )
{ int j = 1, i = 1;
. . .
printf("j=%d i=%d", j, i );
func(j, &i);
printf("j=%d i=%d", j, i );
. . .
}
void func(int j, int *pi)
{ . . .
*pi = 0.5 * j + 1.;
j = 2;
. . .
}
|
Так фрагмент программы в качестве фактических параметров использует входной – переменную j и выходной – адрес переменной i. Этим параметрам в вызываемой подпрограмме соответствуют одноименный входной формальный параметр – переменная j и выходной – указатель pi. В теле подпрограммы численные значения обрабатываемых переменных i и j изменены (i – с помощью разадресации указателя pi, j – напрямую). Выполнение программы приведет к выводу исходных значений j и i (j=1 i=1), а затем их же значений после обращения к дополнительной функции (j=1 i=1.5). Значение входного фактического параметра j не изменилось, а выходного (i) – приняло новое значение. Это произошло потому, что обработка параметра j велась по схеме 1 (разные ячейки), а обработка параметра i – по схеме 2.
Использование выходных параметров освобождает от ограничения на возвращение только одного результата оператором return. Основной результат рекомендуется возвращать оператором return, все остальные в качестве выходных параметров.
Перед составлением программы решения примера 6.3 выполним идентификацию переменных (табл. 6.3).
Таблица 6.3
Обозначение в алгоритме
|
| n
| m
| i
| j
| xi
| yj
|
Обозначение в программе
|
| n
| m
| i
| j
| x[ i ]
| y[ j ]
|
Окончание табл. 6.3
| SX
| PX
| SY
| PY
| d
| k
| z i
| SZ
| PZ
|
| sx
| px
| sy
| py
| d
| k
| z[ i ]
| sz
| pz
|
Анализ алгоритма показывает, что выделить главный из выходных параметров невозможно, т. к. они равнозначны. Поэтому с учетом таблицы идентификации и правил работы с выходными параметрами составленные при выборе метода решения обращения к дополнительной функции примут вид
sp(x, n, &sx, &px); и sp(y, m, &sy, &py);
а заголовок дополнительной функции преобразуется к виду
void sp(float *z, int k, float *sz, *pz)
т. е. входные параметры передаются по значению, а выходные через адреса.
Программа решения примера 6.3
#include <stdio.h> /* stdio.h - файл с прототипами функций ввода-вывода */
#include <conio.h> /* conio.h - файл с прототипом функции getch( ), clrscr( ) */
#include <math.h> /* math.h - файл с прототипами математических функций*/
void sp(float *z, int k, float *sz, float *pz); /*прототип функции sp */
main( ) /* заголовок головной функции */
{
float d, sx, px, sy, py, x[30], y[50]; /* описатели локальных */
int i, j, n, m; /* переменных и массивов */
clrscr( );
printf("\n Введите значения n, m: ");
scanf("%d%d", &n, &m);
fprintf(stdout,"\n n=%d m=%d\n", n, m);
for( i = 0 ; i < n ; i++ ) /* заголовок цикла ввода x[ i ] */
{
printf(" Введите значение x(%d): ", i+1);
scanf("%f", &x[i]);
}
for( i = 0 ; i < n ; i++ ) /* заголовок цикла вывода x[ i ] */
fprintf(stdout," %.2f",x[i]);
printf("\n"); /* перевод курсора в начало следующей строки */
for( j = 0 ; j < m ; j++ ) /* заголовок цикла ввода y[ j ] */
{
printf(" Введите значение y(%d): ", j+1);
scanf("%f", &y[j]);
}
for( j = 0 ; j < m ; j++ ) /* заголовок цикла вывода y[ j ] */
fprintf(stdout," %f",y[j]);
sp(x, n, &sx, &px); /* вызовы дополнительной функции */
sp(y, m, &sy, &py); /* оформленные простыми операторами */
d = (sx + sqrt( fabs( py ) ) ) / ( log( px ) - sy ); /* вычисление d*/
fprintf(stdout,"\n sx=%.2f px=%.2f sy=%.2f py=%.2f d=%.2f \n",
sx ,px, sy, py, d);
getch( );
}
/* определение функции sp */
void sp(float *z, int k, float *sz, float *pz)
{
int i; /* описание локальной переменной i */
*sz = 0;
*pz = 1;
for( i = 0 ; i < k ; i++ ) /* заголовок цикла расчета суммы */
{
*sz = *sz + z[ i ];
*pz = *pz * z[ i ];
fprintf(stdout,"\n %2d %6.2f %8.2f %12.2f ",
i+1, z[ i ], *sz, *pz);
}
}
5 6
1 1.6 1.8 15 23
0.6 0.76 0.99 180 67.7 200
В программе использованы только два массива – на 30 и 50 элементов. Первый из них используется как фактический под именем x и формальный под именем z, второй, аналогично, под именами y и z. Отсутствие оператора return определяется равнозначностью возвращаемых значений.