Составление алгоритма решения. Характерная особенность основного алгоритма при работе с подпрограммой с несколькими возвращаемыми результатами – организация каждого обращения к ней

Характерная особенность основного алгоритма при работе с подпрограммой с несколькими возвращаемыми результатами – организация каждого обращения к ней отдельным блоком «предопределённый процесс». Эта особенность учтена в схеме основного алгоритма. В дополнительном алгоритме локальная переменная i используется для организации цикла перебора текущих значений формального параметра Z. Схемы основного и дополнительного алгоритмов представлены на рис. 10.13.

Программирование задачи

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

Каждая из функций Си/Си++ представляется в этом случае одной из упрощенных структур

[тип] имя([тип b1,...,тип bi,...,тип bn,тип *d1,...,тип *dj,..., тип *dm])

{

тело

функции

[return РВ;]

}

где имя – идентификатор (название) функции;

тип – описатель типа функции (результата);

bi – список входных формальных параметров с указанием типа каждого;

dj – список выходных формальных параметров – указателей, определяющих возвращаемые результа-

Рис. 10.13. Схемы основного и дополнительного алгоритмов задачи о суммах и произведениях

ты, с указанием типа каждого и признака указателя при описании (*);

( ) – ограничители списка формальных параметров;

тело функции – основная часть (совокупность операторов), реализующая вынесенные в отдельный алгоритм вычисления (действия);

return РВ; – оператор возврата в вызывающую функцию результата вычислений РВ (выражения);

[ ] – признак необязательности содержимого;

{ } – ограничители тела функции.

Первая строка структуры – заголовок функции.

Структура вызова функции:

имя ([a1, ..., ai, ..., an,c1, ..., cj, ..., cm])[;]

где имя – идентификатор функции;

a1, …,ai, …,an – список входных фактических параметров (аргументов), численные значения которых требуется передать в дополнительную функцию (подпрограмму);

c1, …, cj,…, cm – список адресов выходных фактических параметров (аргументов), численные значения которых требуется получить из дополнительной функции (подпрограммы);

( ) – ограничители аргументов;

[ ] – признак необязательности содержимого.

Прототип функции аналогичен ее заголовку и имеет структуру:

[тип] имя([тип b1,...,тип bi,...,тип bn,тип *d1,...,тип *dj,...,тип *dm]);

где имя – идентификатор (название) функции;

тип – описатель типа функции (результата);

тип bi – список входных формальных параметров с указанием типа каждого;

тип *dj – список выходных формальных параметров – указателей, определяющих возвращаемые результаты, с записью типа и признака указателя каждого (*);

( ) – ограничители списка формальных параметров;

[ ] – признак необязательности содержимого;

; – признак оператора.

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

· количества и типы входных и выходных формальных параметров должны соответствовать аналогичным фактическим, т.е. каждый ai имеет свой bi, а каждый cj – свой dj, при этом тип и взаимное расположение их в списке определяется программистом;

· в качестве входных формальных параметров используются переменные;

· в качестве выходных формальных параметров используются указатели;

· в качестве входных фактических параметров используются константы, переменные, вызовы функций, выражения и адреса массивов;

· в качестве выходных фактических параметров используются адреса переменных и адреса массивов;

· каждое обращение к подпрограмме, как правило, оформляется как отдельный оператор, в соответствии с требованиями алгоритма;

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

· при использовании структуры с оператором return, вызов функции может служить операндом выражения в вызываемой функции;

· если логика задачи не позволяет выделить основной результат, то использование оператора return необязательно. В этом случае в заголовке дополнительной функции в качестве описателя типа необходимо указывать ключевое слово void;

· вызов функции, оформленный простым оператором, заканчивается символом «;», если же он является операндом выражения – признак оператора не указывается;

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

· в списке формальных параметров прототипа допускается опускать их имена, оставляя для входных их типы, а для выходных типы с последующими символами «*».

Программная реализация задач с использованием входных и выходных параметров опирается на закономерности, описанные при алгоритмизации.

В качестве выходных параметров используются адреса операндов и указатели на них.

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

Принципиальные различия в обработке формальных параметров, заданных переменными или указателями, иллюстрируются схемами рис. 10.14, 10.15.

Рис. 10.14. Схема 1 – передача параметра по значению

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

Рис. 10.15. Схема 2 – передача параметра по адресу

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

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

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

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

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

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

. . . 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.

С учетом изложенного выполним программирование задачи о суммах и произведениях.

Идентификация переменных представлена в табл. 10.3.

Таблица 10.3

Имя в алгоритме n m i j xi yj SX PX SY PY d k z i SZ PZ
Имя в программе n m i j x[i] y[j] sx px sy py d k z[i] sz pz

Предусмотренная алгоритмом обработка двух массивов (на 30 и 50 элементов) позволяет в программе первый из них использовать как фактический под именем x и формальный под именем z, второй, аналогично, под именами y и z.

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

sp(x, n, &sx, &px); и sp(y, m, &sy, &py);

Заголовок дополнительной функции преобразуется к виду:

void sp(float *z, int k, float *sz, *pz)

т. е. входные параметры передаются по значению, а выходные через адреса. Равнозначность возвращаемых значений определяет отсутствие оператора return.

Классический вариант программирования задачи

#include<stdio.h>/*файл с прототипами функций ввода-вывода*/

#include<conio.h>/*файл с прототипом функции getch(), clrscr()*/

#include<math.h> /*файл с прототипами математич. функций*/

#include <windows.h> /*файл с прототипом функции CharToOem*/

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; /* переменных и массивов */

char buf[30];

clrscr( );

CharToOem(" Введите значения n, m: ",buf);

printf("\n %s ",buf);

scanf("%d%d", &n, &m);

printf("\n n=%d m=%d\n", n, m);

for( i = 0 ; i < n ; i++ )/* заголовок цикла ввода x[ i ] */

{

CharToOem(" Введите значение x ",buf);

printf("\n %s (%d): ",buf,i+1);

scanf("%f", &x[i]);

}

for( i = 0 ; i < n ; i++ ) /* заголовок цикла вывода x[ i ] */

printf(" %.2f",x[i]);

printf("\n"); /* перевод курсора в начало следующей строки */

for( j = 0 ; j < m ; j++ ) /* заголовок цикла ввода y[ j ] */

{

CharToOem(" Введите значение y ",buf);

printf("\n %s (%d): ",buf,j+1);

scanf("%f", &y[j]);

}

for( j = 0 ; j < m ; j++ ) /* заголовок цикла вывода y[ j ] */

printf(" %.2f", y[j]);

sp(x, n, &sx, &px); /* вызовы дополнительной функции */

sp(y, m, &sy, &py); /* оформленные простыми операторами */

d = (sx + sqrt( fabs( py ) ) ) / ( log( px ) - sy ); /* вычисление d*/

printf("\n sx=%.2f px=%.2f sy=%.2f py=%.2f d=%.2f \n",

sx ,px, sy, py, d);

getch( );

}

void sp(float *z, int k, float *sz, float *pz)/*определение ф-ции sp*/

{

int i; /* описание локальной переменной i */

*sz = 0;

*pz = 1;

for( i = 0 ; i < k ; i++ ) /* заголовок цикла расчета SZ и PZ */

{

*sz = *sz + z[ i ];

*pz = *pz * z[ i ];

}

}

5 6 – реальные размеры массивов Х и У;

1 1.6 1.8 15 23 – значения элементов массива Х;

0.6 0.76 0.99 180 67.7 200 – значения элементов массива Y;

Под закрывающей скобкой приведены исходные данные для решения задачи.

Результаты решения представлены в приложении 10.5.

Программирование задачи с графическим интерфейсом

Программирование задачи при использовании графического интерфейса предварим его разработкой. Для ввода значений n, m планируем однострочные поля редактирования (EditN, EditM). Для ввода элементов массивов X и Y используем многострочные поля редактирования (EditХ, EditY).

Вывод расчетных значений sx, px, sy, py, d реализуем в однострочные поля редактирования (EditSx, EditPx, EditSy, EditPy, EditD).

Управление процессом решения реализуем двумя командными кнопками, расположенными в нижней части окна. Назначение каждой определяется ее названием.

С учетом планируемого интерфейса выполним программирование задачи.

#include<stdio.h>/*файл с прототипами функций ввода-вывода*/

#include<conio.h>/*файл с прототипом функции getch(), clrscr()*/

#include<math.h>/*файл с прототипами математич. функций*/

void sp(float *z, int k, float *sz, float *pz);/*прототип функции sp*/

void TSumprDlgClient::BNClickedOk()

{

// INSERT>> Your code here.

float d, sx, px, sy, py, x[30], y[50]; /*описатели локальных*/

int i, j, n, m; /* переменных и массивов */

char buf[30];

EditN->GetText(buf,10); /*ввод */

n=atoi(buf); /*значения n*/

EditM->GetText(buf,10); /*ввод */

m=atoi(buf); /*значения m*/

for( i = 0 ; i < n ; i++ ) /* заголовок цикла ввода x[ i ] */

{

EditX->GetLine(buf, sizeof(buf),i);

x[i]=atof(buf);

}

for( j = 0 ; j < m ; j++ ) /* заголовок цикла ввода y[ j ] */

{

EditY->GetLine(buf, sizeof(buf),j);

y[j]=atof(buf);

}

sp(x, n, &sx, &px); /* вызовы дополнительной функции */

sp(y, m, &sy, &py); /* оформленные простыми операторами */

d = (sx + sqrt( fabs( py ) ) ) / ( log( px ) - sy ); /* вычисление d*/

sprintf(buf,"%.2f ",sx); /* вывод */

EditSx->SetText(buf); /*значения sx*/

sprintf(buf,"%.2f ", px); /* вывод */

EditPx->SetText(buf); /*значения px*/

sprintf(buf,"%.2f ",sy); /* вывод */

EditSy->SetText(buf); /* значения sy*/

sprintf(buf,"%.2f ", py); /* вывод */

EditPy->SetText(buf); /* значения py*/

sprintf(buf,"%.2f",d); /* вывод */

EditD->SetText(buf); /* значения d*/

}

/* определение функции 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 и PZ */

{

*sz = *sz + z[ i ];

*pz = *pz * z[ i ];

}

}

5 6 – реальные размеры массивов Х и У;

1 1.6 1.8 15 23 – значения элементов массива Х;

0.6 0.76 0.99 180 67.7 200 – значения элементов массива Y;

Под закрывающей скобкой приведены исходные данные для решения задачи.

Результаты решения представлены в приложении 10.6.

10.4. Подпроцесс с результатом – массивом

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

Постановка задачи

Вычислить суммы элементов каждой строки двумерных массивов A(m x n) и B(t x s). Положительные суммы каждой исходной матрицы сформировать в одномерные массивы.