Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Передача данных между процедурами



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

 

Вызывающая
Вызываемая


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

Результат

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

 

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

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

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

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

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

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

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

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

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

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

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

Пример.

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

double * rasst, *dlina;

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

px=& x;

y=*px;

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

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

В языке Basic можно реализовать оба механизма передачи аргументов. В версиях Visual Studio 2008, 2010 для этого используются ключевые слова ByVal (передача по значению) и ByRef (передача по адресу). По умолчанию в данных версиях, как и в языке С, реализована передача аргументов по значению (в отличие от предыдущих версий).То есть, если перед параметром опущено ключевое слово ByVal или ByRef, то среда подставит слово ByVal. Данных, подобных указателям, нет.

Прототипы функций (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 Указание типа в заголовке функции обязательно. В подпрограммах для указания типа возвращаемого значения (которого нет) используется ключевое слово void. См. выше.

Пример.

float max( float a, float b){

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

}

Basic

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

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

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( int m, int n, float a[ ][10], int * k, int * l){

float max;

int i, j;

max = a[0][0];

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

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

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

}

}

return max;

}// End maxmatr

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

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

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

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


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

str, col; // Индексы максимального элемента

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

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

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

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

void maxmatr( int m, int n, float a[ ][10], int *k, int * l, float * maxim){

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

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

Пример.

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

Basic

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

Пример.

Sub maxmatr( ByVal m As Integer, ByVal n As Integer, ByRef a(, ) As Single, _

ByRef k As Integer, ByRef l As Integer, ByRef max As Single )

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

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.

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

int main( ){

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

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

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

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

int i, j;

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

void matrix( float [ ][4], float [ ], float [ ]); // Прототип

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);

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

}// 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

Замечание. Приведенный пример решает поставленную задачу только для размера матрицы 4х4, что не позволяет использовать данное решение для матриц других размеров. Это резко снижает область его применения (свойство массовости алгоритма). Для повышения универсальности алгоритма при использовании массивов в языке C применяют стандартный прием: объявляют массив максимально ожидаемого размера (проигрывая в памяти), затем задают реальные его размеры, контролируя непревышение выделенной памяти. Ниже приводится решение для максимального размера матрицы 30х30.

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

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

int main( ){

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

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

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

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

int i, j,

m; // Размер матрицы

bool fl; // true-неправильный ввод

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

void matrix( int , float [ ][30], float [ ], float [ ]);

fl= true;

while (fl){

clrscr( );

printf(" Размер матрицы: " ); scanf(" %d", & m);

if (m< 1||m> 30){

printf(" Размер матрицы должен быть в диапазоне [1: %d]\n", 30);

getch();

} else fl= false ;

}

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

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

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

}

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

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

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

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

}

}

matrix(m, b, u, v);

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

getch( );

}// End main


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

void matrix( int m, floa t a[ ][30], float x[ ], floa t y[ ]){

int i, j;

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

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

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

}

}

}// End matrix

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

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

int i;

float z;

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

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

}

return z;

}// End scalar

Аналогичный прием можно применять и в программировании на языке Basic, однако там имеется возможность при задании размеров выделять память под массивы в соответствии с ними.

Basic

В заголовке вызываемой процедуры для параметра-массива следует использовать ключевое слово ByRef. При этом копирования всего массива в локальную память процедуры не происходит и изменение его содержания приводит к изменению соответствующего аргумента в вызывающей процедуре.

Пример. Реализация предыдущей задачи. Только модуль Main.

Imports System.Console

Module Main

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

Sub main( )

Dim i, j, m As Short ‘ Размер матрицы

Dim fl As Boolean ‘ true-неправильный ввод

fl= True

Do While fl

Clear( )

Write(" Размер матрицы: " ): m=ReadLine( )

If m< 1

WriteLine(" Размер матрицы должен быть > 0”)

ReadLine( )

Else

fl= False

End If

Loop

‘Объявление массивов

Dim u(m-1) As Single ‘ Входной вектор

Dim b(m-1, m-1) As Single ‘ Входная матрица

Dim v(m-1) As Single ‘ Вектор b*u

WriteLine(" Исходный вектор (вводить столбиком)" )

For i=0 To m-1

u( i )=ReadLine( )

Next

WriteLine(" Исходная матрица (вводить столбиком)" )

For i=0 To m-1

For j=0 To m-1

b( i, j)= ReadLine( )

Next j

Next i

matrix(m, b, u, v)

WriteLine( )

WriteLine(" Квадратичная форма равна {0: g5}", scalar(m, v, u))

ReadLine( )

End Sub

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

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

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

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

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

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

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

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

float f1( int, float [ ]), // Минимизируемая функция

min_fun( int, float [ ], float, float, float (*)( )) // Процедура минимизации

z;

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

z=min_fun(l, s, delta_x, epsilon, f1); // Обращение к процедуре

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

float min_fun( int n, // Число измерений

float x[ ], // Начальное приближение

//Результат: точка минимума

float dx, // Начальный шаг поиска

float eps, // Заданная точность нахождения минимума

float (*f)( )){// Адрес минимизируемой функции

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

}

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

< тип> *f( ); и < тип> (*f)( );

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

Пример. Вычислить и напечатать таблицу функции:

Интеграл вычислять методом трапеций.

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

int main( ){

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

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

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

trap( double, double, int, double (*)( 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 )){// Подинтегральная функция

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));

} // End f1

Basic

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

Формат объявления:

Delegate { Function | Sub } < имя> ([< список параметров> ]) [ As < тип> ]

Для подпрограммы тип возвращаемого значения отсутствует.

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

Ниже приводится решение предыдущей задачи на языке Basic.

Module Main

‘ Объявление делегата

Delegate Function UndInteg( ByVal x As Double ) As Double

Sub Main()

Dim y As Double ' Значение интеграла

Dim alfa As Double ' Параметр

Dim DelF1 As UndInteg ' Переменная типа делегата

DelF1 = AddressOf F1 ' Задание адреса функции-аргумента

For alfa = 2 To 3.05 Step 0.1

y = Integr(0.15, alfa, 20, DelF1)

Console.WriteLine(" альфа={0: f1} интеграл={1: f4}", alfa, y)

Nex t

Console.ReadLine()

End Sub

End Module


Module Integ

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

' a-нижний предел, b-верхний предел, k-число элем.интервалов, F1-адрес

‘ подинтегральной функции

Function Integr( ByVal a As Double, ByVal b As Double, _

ByVal k As Short, ByRef F As UndInteg)

Dim dx As Double 'Размер элементарного интервала

dx = (b - a) / k

Integr = (F(a) + F(b)) / 2

For i As Integer = 1 To k - 1

Integr += F(a + i * dx)

Next

Return Integr *dx

End Function

End Module

Imports System.Math

Module UnderInteg

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

Function F1( ByVal x As Double ) As Double

F1 = Exp(x) * Cos(x^3))

End Function

End Module

Совет. Сравните реализации, найдите отличия, запомните и решите задачу ВЦ 24 с использованием делегата.

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

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

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

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

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

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

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

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

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

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

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

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

4. Укажите отличия передачи аргументов по значению и по адресу.

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

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

7. Что такое делегат и для чего он нужен?

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

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


Поделиться:



Последнее изменение этой страницы: 2017-04-12; Просмотров: 549; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.179 с.)
Главная | Случайная страница | Обратная связь