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

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

ü Внимание! Если размер выходного массива совпадает с одним из параметров входного, то указывать его не требуется.

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

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

Методика хранения многомерных массивов (разд. 9.1.1.2) позволяет рассчитывать адрес каждого элемента через начальный адрес массива, добавляя к нему сумму произведений увеличенных размеров на длину ячейки для хранения каждого элемента.

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

Так, при передаче в дополнительную функцию двумерного массива A(mxn) с максимально требуемым размером 10x15, фрагмент обращения имеет вид

float a[10][15];

. . .

func(a[0], m, n, 15);

. . .

а заголовок дополнительной функции запишется, например, следующим образом

void func(float *z, int k, int p, int t)

при этом обращение к текущему элементу zij имеет вид

*(z + i * t + j)

С учетом изложенного, выполним программирование задачи 10.4. Идентификация переменных представлена в табл. 10.4.

Таблица 10.4

Обозначение в алгоритме m n t s i j aij bij
Обозначение в программе m n t s i j a[i][j] b[i][j]

Окончание табл. 10.4

ssai ssbi mp tp k p ss d sszd zij
ssa[i] ssb[i] mp tp k p ss d ssz[d] z[i][j]

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

sum_str( a, m, n, 15, ssa, &mp); и sum_str( b, t, s, 7, ssb, &tp);

где m, n, t, s – фактические используемые размеры, 15 и 7 – фактические максимальные размеры по второму измерению массивов A и B, а &mp и &tp – адреса размеров возвращаемых массивов.

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

void sum_str(float *z, int k, int p, int pmax, float *ssz, int *d)

где k и p – формальные используемые размеры, а pmax – формальный максимальный размер по второму измерению массива Z, *d – указатель размера создаваемого массива.

Универсальные размеры массивов зададим подстановочными директивами define, что позволит модифицировать увеличенные размеры всех массивов в заголовочной части программы.

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

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

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

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

#define M 10 /* присваивание*/

#define N 15 /*максимальных*/

#define T 9 /*размеров*/

#define S 7 /*массивов А и В*/

void sum_str(float* z, int k, int p, int pmax, float *ssz, int *d);

main( ) /* заголовок головной функции */

{

float ap, bp, a[M][N], b[T][S], ssa[M], ssb[T]; /*описатели */

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

char buf[30];

clrscr( );

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

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

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

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

CharToOem(" Введите построчно массив A ",buf);

printf("\n %s (%d*%d):\n", buf, m, n);

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

for( j = 0 ; j < n ; j++ )/*заголовок внутрен. цикла ввода a[i][j]*/

scanf("%f", a[0] + i * N + j );

CharToOem("Массив A ",buf);

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

for( i = 0 ; i < m ; i++ )/* заголовок внешн. цикла вывода a[i][j]*/

{

for( j = 0 ; j < n ; j++ )/*заголовок внутр. цикла вывода a[i][j]*/

printf(" %6.2f", a[i][j]);

printf("\n");

}

CharToOem(" Введите построчно массив B ",buf);

printf("\n %s (%d*%d):\n", buf, t, s);

for( i = 0 ; i < t ; i++ ) /* заголовок внешнего цикла ввода b[i][j]*/

for( j = 0 ; j < s ; j++ ) /* заголовок внутр. цикла ввода b[i][j]*/

scanf("%f", b[0] + i * S + j );

CharToOem("Массив B ",buf);

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

for( i = 0 ; i < t ; i++ ) /* заголовок внешн. цикла вывода b[i][j]*/

{

for( j = 0 ; j < s ; j++ ) /* заголовок внутр. цикла вывода b[i][j]*/

printf(" %6.2f", b[i][j]);

printf("\n");

}

sum_str( a[0], m, n, N, ssa, &mp);

CharToOem("Массив SSA ",buf);

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

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

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

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

sum_str( b[0], t, s, S, ssb, &tp);

CharToOem("Массив SSB ",buf);

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

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

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

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

getch( );

}

 

void sum_str(float *z, int k, int p, int pmax, float *ssz, int *d)

{

int i, j; /* описание локальных */

float ss; /* переменных */

*d = 0;

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

{

ss = 0;

for( j = 0 ; j < p ; j++ ) /* заголовок внутр. цикла расчета ss */

{

ss = ss + *(z + i * pmax + j);

}

if( ss >= 0 )

{

ssz[*d] = ss;

*d=*d + 1;

}

}

}

3 4 2 3 – реальные размеры массивов А и В;

8.53 9.3 5.7 -3.5 – значения

46 -32.1 28.5 -52.6 элементов

4.7 56 65 -7.2 массива А;

1.6 7.3 15 – значения элементов

4.2 -10.18 12 массива В.

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

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

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

Программирование задачи при использовании графического интерфейса предварим его разработкой.

ListBoxB
ListBoxA

Для ввода значений n, m, t, s планируем однострочные поля редактирования (EditN, EditM, EditT, EditS). Для ввода элементов массивов A и B используем многострочные поля редактирования (EditA, EditB). Вывод форматированных массивов А и В и расчетных значений ssa и ssb реализуем в поля-списки (ListBoxA, ListBoxB, ListBoxSSA, ListBoxSSB).

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

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

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

#define M 10 /* присваивание*/

#define N 15 /*максимальных*/

#define T 9 /*размеров*/

#define S 7 /*массивов А и В*/

void sum_str(float* z, int k, int p, int pmax, float *ssz, int *d);

void TSumprDlgClient::BNClickedOk()

{

// INSERT>> Your code here.

float ap, bp, a[M][N], b[T][S], ssa[M], ssb[T]; /* описатели */

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

char buf[15], buf1[50]="";

ListBoxA->ClearList();

ListBoxB->ClearList();

ListBoxSSA->ClearList();

ListBoxSSB->ClearList();

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

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

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

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

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

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

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

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

for( i = 0 ; i < m ; i++ )/*заголовок внешн. цикла ввода a[i][j]*/

for( j = 0 ; j < n ; j++ )/*заголовок внутрен. цикла ввода a[i][j]*/

{

EditA->GetLine(buf, sizeof(buf),i*n+j);

a[i][j] = atof(buf);

}

for( i = 0 ; i < m ; i++ )/* заголовок внешн. цикла вывода a[i][j]*/

{

strcpy(buf1, "");

for( j = 0 ; j < n ; j++ )/*заголовок внутр. цикла вывода a[i][j]*/

{

sprintf(buf,"%.2f ", a[i][j]);

strcat(buf1, buf); /*слияние строк buf и buf1*/

}

ListBoxA->AddString(buf1);

}

for( i = 0 ; i < t ; i++ )/*заголовок внешн. цикла ввода b[i][j]*/

for( j = 0 ; j < s ; j++ )/*заголовок внутр. цикла ввода b[i][j]*/

{

EditB->GetLine(buf, sizeof(buf),i*s+j);

b[i][j] =atof(buf);

}

for( i = 0 ; i < t ; i++ )/* заголовок внешн. цикла вывода b[i][j]*/

{

strcpy(buf1, ""); /*очистка содержимого строки buf1*/

for( j = 0 ; j < s ; j++ ) /* заголовок внутр. цикла вывода b[i][j]*/

{

sprintf(buf,"%.2f ", b[i][j]);

strcat(buf1, buf); /*слияние строк buf и buf1*/

}

ListBoxB->AddString(buf1);

}

sum_str( a[0], m, n, N, ssa, &mp);

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

{

sprintf(buf,"%.2f ", ssa[i] );

ListBoxSSA->AddString(buf);

}

sum_str( b[0], t, s, S, ssb, &tp);

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

{

sprintf(buf,"%.2f ", ssb[i] );

ListBoxSSB->AddString(buf);

}

}

void sum_str(float *z, int k, int p, int pmax, float *ssz, int *d)

{

int i, j; /* описание локальных */

float ss; /* переменных */

*d = 0;

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

{

ss = 0;

for( j = 0 ; j < p ; j++ ) /* заголовок внутр. цикла расчета ss */

{

ss = ss + *(z + i * pmax + j);

}

if( ss >= 0 )

{

ssz[*d] = ss;

*d=*d + 1;

}

}

}

3 4 2 3 – реальные размеры массивов А и В;

8.53 9.3 5.7 -3.5 – значения

46 -32.1 28.5 -52.6 элементов

4.7 56 65 -7.2 массива А;

1.6 7.3 15 – значения элементов

4.2 -10.18 12 массива В.

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

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

Заключение

Процесс – основные (главные) вычисления, реализующие общую цель задачи.

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

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

Взаимодействие процесса с подпроцессом – обращение. Обращение – указание перехода из процесса в подпроцесс детализации некоторого фрагмента вычислений.

Особенность алгоритмизации процессов с подпроцессами – создание нескольких отдельных алгоритмов (основного и вспомогательных). Основной – алгоритм реализации главного вычислительного процесса. Вспомогательный (дополнительный) – алгоритм реализации отдельного вычислительного подпроцесса.

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

Основная базовая конструкция языка Си/Си++ – функция. Функция – программный модуль, реализующий некоторый обособленный участок вычислений, оформленный отдельным алгоритмом. Головная функция – основной программный модуль, из которого организуется вызов дополнительных функций. Дополнительная функция – программный модуль, реализующий конкретный подпроцесс (дополнительный алгоритм).

Вызов функции – обращение к ней для передачи значений фактических параметров и получения результатов её работы. Функции Си/Си++ – вызывающие и вызываемые. Вызывающая – содержащая вызов (вызовы) любой другой. Вызываемая – к которой обращен вызов (активизируемая им).

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

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

Организация процессов с подпроцесами реализует модульный принцип построения программного продукта, структурируя решаемую задачу. Эффективность использования процессов с подпроцессами наивысшая по отношению ко всем рассмотренным ранее. Глобализация принципов модульного программирования – основа объектно-ориентированного проектирования.

Вопросы для контроля

1. Какой алгоритм – основной, а какой – дополнительный?

3. Какова структура обращения к дополнительному алгоритму?

4. Какие параметры называют фактическими, формальными?

6. Какова схема взаимодействия головного алгоритма с дополнительными?

7. Чем головная программа отличается от подпрограммы?

8. На какие типы делятся подпрограммы?

9. Что такое функция, как она вызывается?

11. Для чего нужен прототип функции?

12. Какова структура программы с подпрограммами?

13. Какова структура определения функции?

16. Как осуществляется передача массивов в функцию?

17. Что такое указатель, какова структура описания указателя?

19. Для чего нужны операции взятия адреса и разадресации?

20. Чем отличаются входные параметры от выходных?

21. Каков механизм передачи в функцию выходных параметров?

22. Чем отличается передача параметров по значению от передачи по адресу?