Связь между вызывающей и вызываемой функциями 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Связь между вызывающей и вызываемой функциями



Схема обмена данными между процедурами:

 


Входные аргументы Выходные аргументы

Результат

Исходные данные Результаты

Входные параметры Выходные параметры

 

Возвращаемое значение функции разумно считать специфическим выходным параметром.

Механизмы передачи данных

Существуют 2 механизма передачи информации между процедурами: по значению и по адресу (ссылке, имени, наименованию).

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

Во втором механизме передается адрес аргумента и, следовательно, возможно его изменение в вызываемой процедуре. Этот механизм применяется для выходных данных (результатов работы) вызываемой процедуры.

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

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

Формат определения указателей:

<тип> *<имя>[, *<имя>]...;

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

Пример.

int *kol, *nom, x, y, *px;

double * rasst, *dlina;

..................................

px=&x;

y=*px;

Это эквивалентно y=x.

Более подробно работа с указателями будет рассмотрена в другом разделе.

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

Прототипы функций (C)

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

Формат прототипа:

<тип><имя>(<тип_параметра>[<имя>][, <тип_параметра>[<имя>]]...);

Пример. Прототип функции вычисления определенного интеграла методом трапеций.

double trap(double, double, int, double (*)(double));

Вызов функции:

.................................................................................

i=trap(0, alfa, 20, f1);/* Без прототипа ошибка: 0 – целое значение*/

.................................................................................

Если типы аргумента и соответствующего параметра не совпадают, то выполняется в соответствии с прототипом допустимое преобразование и в память, выделяемую транслятором этому параметру, попадает уже преобразованное значение. Аналогичные манипуляции производятся при возврате результатов в вызывающую процедуру. Имя параметра в прототипе указывать необязательно, сущесвенно лишь задание его типа.

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

Пример. Все математические функции имеют в качестве параметров и возвращаемого значения данные типа double. Их прототипы хранятся в заголовочном файле math.h. Например, double sin( double );

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

Передача скаляров

Возвращаемое значение

C

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

Пример.

float max( float a, float b){

........................

}

Basic

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

Входные данные

C

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

Пример.

Вызывающая процедура Вызываемая процедура

float a, max( float, float ); float max( float a, float b){

int b;

.........................................................

y=2+3.5*max(a, b); // Целая переменная b преобразуется к типу float

}


Basic

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

Пример.

function Max(ByVal a as double, ByVal b as double) as double

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

Пример.

y=2+3.5*max((a), (b))

Выходные данные

C

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

Пример. Дана матрица {aij}, i,j=1...10. Найти max{aij} и его индексы.

float maxmatr(float a[10][10], int * k, int * l){

float max;

int i, j;

max = a[0][0];

for (*k=*l=i=0; i<10; i++){

for (j=0; j<10; j++){

if (max<a[ i ][ j ]){max=a[ i ][ j ]; *k=i; *l=j; }

}

}

return max;

}/* End maxmatr */

Соответствующий фрагмент вызывающей процедуры имеет вид:

float maxmatr(float a[ ][10], int *, int*), // Прототип

maxim, // Максимальный элемент

a[10][10]; // Исходная матрица

int m, n; // Ее размеры

..................................

maxim=maxmatr(a, & m, & n);

Употребление конструкции float a[ ][10] будет пояснено ниже.

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

void maxmatr(float a[10][10], int *k, int * l, float * max){

float * max;

......................

Также везде надо заменить max на *max и убрать инструкцию return.

Пример.

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

Basic

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


Передача массивов

C

Если аргумент процедуры – массив, то используется механизм передачи по адресу. При этом в список аргументов включается имя массива, в вызываемую процедуру передается адрес первого элементанулевыми индексами) и массив некопируется в локальную память функции. При описании float a[10][10] обращение вида: <имя_функции>(a) эквивалентно обращению: <имя_функции>(& a[0][0]). Следовательно, массивы-параметры занимают память, отводимую в вызывающей процедуре массивам-аргументам, поэтому в вызываемой процедуре допустимы описания вида float b[], a[][10]; Длины всех измерений, кроме первого, надо задавать, чтобы правильно извлечь из памяти значения нужного элемента массива (см. формулу в параграфе "Распределение массивов"). Допустимо даже несоответствие размерностей аргумента и параметра.

Пример.

Аргументы Параметры

float a[5][5], b[36]; float a[ ], b[ ][6];

Пример. Вычислить: z=uТbu, где {ui]}, i=1...4; {bij]}, i,j=1...4.

/* Вычисление квадратичной формы */

void main( void ){

float u[4], /* Входной вектор */

b[4][4], /* Входная матрица */

v[4], /* Вектор b*u */

z, /* Результат */

scalar( float u[ ], float v[ ]); /*Скалярное произведение векторов */

int i, j;

/* Умножение матрицы на вектор */

void matrix(float b[ ][4], float u[ ], float v[ ]);

printf("Исходный вектор:\n");

for (i=0; i<4; i++){

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

}

printf("Исходная матрица:\n");

for (i=0; i<4; i++){

for (j=0; j<4; j++){

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

}

}

matrix(b, u, v);

z=scalar(v, u);

printf("\n\n\nКвадратичная форма равна %.5g\n", z);

}/* End main */

/* Умножение матрицы на вектор */

void matrix(floa t a[ ][4], float x[ ], floa t y[ ]){

int i, j;

for (i=0; i<4; i++){

for (y[ i ]=j=0; j<4; j++){

y[ i ] += a[ i ][ j ]*x[ j ];

}

}

}/* End matrix */

/* Скалярное произведение векторов */

float scalar (float x[ ], float y[ ]){

int i;

float z;

for (z=i=0; i<4; i++){

z+=x[ i ]*y[ i ];

}

return z;

}/* End scalar */

Basic

Массивы передаются также по адресу, поэтому изменение их в вызывающей процедуре затруднений не вызывает.

Дальнейший материал относится только к языкам C и C++. В языке Basic подобных средств нет.

Передача функций

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

Этот специфический вид аргумента позволяет придать программе универсальность.

Пример. Найти y=min(f(x)), где {xi}, i=1...n. Существует множество методов нахождения экстремума функции многих переменных, практически не зависящих от вида функции, минимум которой отыскивается.

Сопряжение, т.е. имя функции со списком аргументов, min_fun(x, n, dx, eps, f) практически одинаково для различных методов. Здесь вектор (одномерный массив) x является одновременно и входным параметром (начальное приближение), и выходным (найденная точка минимума); n – число координат; dx – начальный шаг поиска; eps – точность нахождения минимума; f – имя минимизируемой функции.

Передаваемое значение является адресом функции. Следовательно, соответствующий параметр – указатель на функцию. Это специфический объект, характерный только для языков C и С++.

Формат объявления: [<тип>](*<имя>)();

Чтобы при вызове в вызывающей процедуре имя функции-аргумента не рассматривалось бы транслятором как простая переменная, необходимо в ней объявить прототип минимизируемой функции:

float f1(float [ ]), min_fun(float [ ], int, float, float, float (*)()), z;

..........................

z=min_fun(s, l, delta_x, epsilon, f1);

Вызываемая процедура:

float min_fun(float x[ ], int n, float dx, float eps, float (*f)()){

..........................

}

Следует раличать записи:

[<тип>] *f( void ); и [<тип>](*f)( void );

Первая – прототип функции без параметров, возвращающей указатель на <тип>, вторая – прототип указателя на функцию, возвращающей значение данного типа.


Пример. Вычислить и напечатать таблицу функции: Интеграл вычислять методом трапеций.

/* Вычислить таблицу y=f(alfa) */

void main(void){

double f1(double), // Подинтегральная функция

y, // Значение интеграла

alfa, // Параметр

trap(double, double, int, double (*)()); // Метод трапеций /

for (alfa=2; alfa<3.05; alfa+=.1){

y=trap(.15, alfa, 20, f1);

printf("%10cальфа=%.1lf интеграл=%.6lf\n", ' ', alfa, y);

}

} // End main

/* Интегрирование методом трапеций */

double trap(double a, // Нижний предел интегрирования

double b, // Верхний --------------------

int k, // Число элементарных интервалов

double (*f)()){ // Подинтегральная функция

double dx, // Размер элементарного интервала

t;

int i;

dx=(b-a)/k;

t=((*f)(a)+(*f)(b))/2;

for (i=1; i<k; i++){

t += (*f)(a+i*dx);

}

return dx*t;

} /* End trap */

/* Подинтегральная функция */

double f1 (double x){

return exp(x)*cos(pow(x, 3));//Без прототипов все эти функции возвращают // int

} /* End f1 */

Вопросы для самопроверки и контроля

Вопросы для самопроверки

1. Что такое блок?

2. Есть ли в языке Basic главная процедура?

3. В каком языке не определено понятие подпрограммы?

4. Что такое указатель на функцию?

5. Чем являются выходные скалярные параметры в языке C?

6. Что передается в вызываемую процедуру, если аргумент – имя массива?

Контрольные вопросы

1. Чем отличается статическое и динамическое распределение памяти?

2. Что является минимальным элементом структуры программы в языке C?

3. Зачем нужны процедуры?

4. В чем отличие передачи аргументов по значению от передачи по адресу?

5. Перечислите различия в инструкциях возврата значения функции в языках Basic и C.

6. Является ли возвращаемое значение функции выходным параметром?

ПРЕПРОЦЕССОР

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



Поделиться:


Последнее изменение этой страницы: 2017-02-08; просмотров: 319; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.17.6.75 (0.098 с.)