Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции программирование.doc
Скачиваний:
38
Добавлен:
05.11.2018
Размер:
4.73 Mб
Скачать

Составление алгоритма решения

С учётом выбранных обозначений составим схемы основного и дополнительного алгоритмов (рис. 6.4). Используемые в дополнительном алгоритме переменные s и i являются локальными. Печать промежуточных значений i, ti, s предусмотрена для облегчения отладки программы.

Рис. 6.4. Схемы основного и дополнительного алгоритмов примера 6.2

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

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

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

Следовательно, обращение, записанное в Си для передачи массива X(n), имеет вид sum( x , n ), а для массива Y(m) – sum( y , m ).

  • Внимание! В списке фактических параметров идентификатор одномерного массива однозначно подразумевает адрес его первого элемента.

Поэтому имя может быть заменено адресом первого элемента массива, т. е. рассмотренные обращения можно записать как sum( &x[0] , n ) и sum( &y[0] , m ).

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

Указатель – переменная для хранения адреса.

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

Указатели, как и простые переменные, обозначаются именами (идентификаторами). Имена задаются самим пользователям по обычным для переменных правилам.

Указатели описываются аналогично переменным и массивам. Тип указателя определяется типом переменной (массива), на которую он ссылается.

Структура описания указателя:

описатель * иу1 [, * иу2, . . . , * иуN ];

где описатель – ключевое слово, определяющее тип указателя;

* – признак указателя при описании;

иу1... иуN – идентификаторы указателей;

, – разделитель списка идентификаторов;

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

; – символ окончания оператора описания.

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

Например, описатели

float *a, *b;

int *f;

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

Описатели

float x, y, *a, *b,

int *f, arr[10];

наряду с указателями задают типы переменных x, y и целочисленного массива arr. Месторасположение указателя в списке совместного описания задается произвольно.

Соответствие указателя и адреса переменной, на которую он ссылается, выражается зависимостью

иу = &ип

где иу – идентификатор указателя;

& – операция взятия адреса;

ип – идентификатор переменной.

Например, оператор

b = &x;

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

Операторы

f = &arr[0]; или f = arr;

задают указателю f значение адреса массива arr (его первого элемента).

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

Разадресация предписывает получение содержимого переменной (ячейки оперативной памяти), на которую ссылается указатель. Разадресация выполняется указанием символа звездочка (*) перед именем указателя.

Запись разадресации имеет вид

*иу

где иу – идентификатор указателя;

* – символ операции разадресации.

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

Например, фрагмент программы использования операции разадресации для получения значения

float g, s, *t;

. . .

g = 15.3;

t = &g;

s = sqrt( *t ) + *t + 0.5;

. . .

описывает переменные g, s и указатель t как вещественные, присваивает указателю t адрес переменной g, а затем, используя операцию разадресации указателя t, формирует операнды выражения s – подкоренное выражение и второе слагаемое – как константы 15.3 (содержимое переменной g).

Фрагмент

float x, z, *d;

. . .

d = &z;

*d = pow( x , 2 ) + 5;

. . .

поясняет использование операции разадресации для записи константы (результата вычисления выражения pow( x , 2 ) + 5) в ячейку переменной z с использованием указателя d.

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

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

Элементы переданного в дополнительную функцию массива могут использоваться напрямую (указанием индексного выражения) или с помощью операции разадресации.

. . .

float funk( float* , int );

main( )

{ float t, x[10];

. . .

n = 10;

t = funk( x , n );

. . .

}

float funk( float *px, int n1 )

{ . . .

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

f = . . . + *(px+i) + . . . .;

. . .

return f;

}

Так, фрагменты программы

. . .

float funk( float *px , int n1 );

main( )

{ float t, x[10];

. . .

n = 10;

t = funk( x , n );

. . .

}

float funk( float *px, int n1 )

{ . . .

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

f = . . . + px[i] + . . . .;

. . .

return f;

}

поясняют варианты передачи одномерных массивов в функцию, использования её элементов и способы написания прототипа. Во втором варианте прототипа тип указателя дополнен обязательным элементом – знаком «*».

В дополнительной функции вызов i-го элемента массива осуществляется двумя способами:

  1. индексированной переменной;

  2. полным индексным выражением.

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

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

Перед составлением программы решения выполним идентификацию переменных (табл. 6.2).

Таблица 6.2

Обозначение в алгоритме

1

a

b

n

m

i

j

xi

Обозначение в программе

2

a

b

n

m

i

j

x[i]

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

1

yj

z1

z2

z

k

ti

s

2

y[j]

z1

z2

z

k

t[i]

s

Программа решения примера 6.2

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

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

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

float sum(float *t, int k); /* прототип пользовательской функции */

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

{

float a, b, z1, z2, z, x[10], y[15]; /* описатели локальных */

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

clrscr( );

printf("\n Введите значения a, b, n, m: ");

scanf("%f%f%d%d", &a, &b, &n, &m);

fprintf(stdout,"\n a=%.2f b=%.2f n=%d m=%d\n", a, b, 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]);

z1 = cos(a) + sum( x, n ); /* вычисление с обращением к */

z2 = sum( y , m ) - b; /* дополнительным функциям */

z = z1 / z2;

fprintf(stdout,"\n\n z1=%.2f z2=%.2f z=%.2f\n", z1, z2, z);

getch( );

}

/* определение дополнительной функции расчёта суммы элементов массива */

float sum(float *t, int k ) /* заголовок дополнительной функции */

{

float s; /* описание локальных */

int i; /* переменных s и i */

s = 0;

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

{

s = s + t[ i ];

fprintf(stdout,"\n %2d %6.2f %8.2f ", i+1 , t[ i ], s);

}

return s; /* возвращение значения s в вызывающую функцию */

}

0.96 35. 5 4

4.5 12.3 -0.8 17 0.3

45.3 -0.3 12.7 2.5

Подпрограмма с несколькими результатами

Этот вариант подпрограммы (дополнительной функции) применяют, когда в дополнительный алгоритм выносится участок вычислений с несколькими конечными результатами. Рассмотрим программирование подобного класса задач на конкретном примере 6.3.

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

Вычислить значение функции:

если .

Формирование математической модели

Ввиду того, что общая математическая формулировка выполнена в постановке задачи, дополним её конкретными размерами массивов (n = 5, m = 6) и численными значениями элементов:

x1=1; x2=1,6; x3=1,8; x4=15; x5=23;

y1=0,7; y2=0,76; y3=0,99; y4=180; y5=67,7; y6=200.

Выбор метода решения

Анализ показывает, что решение задачи требует двукратного вычисления суммы и произведения элементов массива. В первом случае n элементов массива X, во втором – m элементов массива Y. Такое вычисление удобно выполнить в циклическом процессе, оформленном дополнительным алгоритмом. Так как результат вычислений в дополнительном алгоритме (подпрограмме) – две величины, то решение задачи требует использования дополнительной функции, возвращающей более одного результата.

Особенностью подпрограмм с несколькими возвращаемыми значениями является использование в них входных и выходных формальных параметров.

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

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

Для рассматриваемой задачи в качестве входных формальных параметров выберем, например, имя массива Z и его размер k. Тогда в качестве выходных параметров можно использовать SZ (сумма Z) и PZ (произведение Z). Следовательно, в качестве формальных параметров выбраны Z, k, SZ, PZ.

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

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

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

Поэтому, задавшись именем подпрограммы sp, сформируем два обращения к ней: для расчёта суммы и произведения элементов массива X – sp(X(n), SX, PX) и для тех же вычислений с массивом Y – sp(Y(m), SY, PY). Первый параметр в каждом обращении является входным и определяет имя и размер передаваемого в подпрограмму массива. Остальные являются выходными, предназначенными для получения значений суммы и произведения из подпрограммы.