Особенностью рассматриваемой задачи является необходимость использования в качестве формальных и фактических параметров имен и размеров массивов.
В языке Си при передаче массива в дополнительную функцию в качестве фактических параметров выступают адрес расположения массива в оперативной памяти и фактический размер массива.
Следовательно, обращение, записанное в Си для передачи массива 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-го элемента массива осуществляется двумя способами:
5. индексированной переменной;
6. полным индексным выражением.
В любом случае имя указателя идентифицирует массив (адрес первого элемента), а индекс i – смещение текущего элемента относительно первого. В первом случае значение текущего элемента массива вызвано через автоматически сформированный адрес, а во втором – через операцию разадресации.
Для одномерных массивов в большинстве случаев используют первый способ как более привычный и компактный.
Перед составлением программы решения выполним идентификацию переменных (табл. 6.2).
Таблица 6.2
Обозначение в алгоритме
|
| a
| b
| n
| m
| i
| j
| xi
|
Обозначение в программе
|
| a
| b
| n
| m
| i
| j
| x[i]
|
Окончание табл. 6.2
| yj
| z1
| z2
| z
| k
| ti
| s
|
| 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