Следовательно, в функцию необходимо наряду с используемым фактическим размером передавать и максимальный, указанный в описателе
Если возвращаемые результаты не требуется хранить для последующего использования, достаточно описать один массив и использовать его многократно (последовательно при каждом обращении к функции). Если все возвращаемые массивы результатов требуется хранить, в вызывающей функции каждый из них должен быть описан как самостоятельный с уникальным именем и размером, определяемым логикой задачи. В этом случае в каждом обращении к дополнительной функции используется своё имя и размер. Если фактический размер выходного массива заранее известен, передача его осуществляется по значению, если рассчитывается в дополнительной функции – передаётся (возвращается) по адресу.
ü Внимание! Если размер выходного массива совпадает с одним из параметров входного, то указывать его не требуется.
В рассматриваемом примере используются два входных двумерных (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.
Программирование задачи с графическим интерфейсом
Программирование задачи при использовании графического интерфейса предварим его разработкой.
|
|
Для ввода значений 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. Чем отличается передача параметров по значению от передачи по адресу?