Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ответы.docx
Скачиваний:
335
Добавлен:
22.03.2016
Размер:
1.03 Mб
Скачать

73. Завершение работы функции

См п.72

74. Механизмы передачи данных через параметры функций

75. Передача данных по значению

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

void F (int I)

{

I = I + 20;

cout << I << endl;

}

int main()

{

int A = 10;

cout << A << endl; // Выведено значение 10

F ( A ); // Выведено значение 30

cout << A << endl; // Выведено значение 10

return 0;

}

В этом примере значение переменной А до вызова функции F и после завершения работы этой функции одинаковы. При выполнении функции значение параметра I изменяется, но это изменение не затрагивает значение переменной A, так как функция работает не с самой переменной A, а с ее копией, которая представляется параметром I.

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

76. Передача данных через указатели

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

Решение этой проблемы: необходимо заставить функцию обрабатывать не копию аргумента, а само значение аргумента.

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

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

int Div (int N1, int N2, int *Ost) // int *Ost – параметр-указатель

{

*Ost = N1 % N2; // *Ost – разыменование параметра-указателя

return N1 / N2;

}

int main()

{

int I = 10, J = 3, R, O;

R = Div (I, J, &O); // &O – адрес аргумента O

cout << I << “ / ” << J << “ = ” << R << “. Остаток равен “ << O << endl;

return 0;

}

Функция Div находит результат и остаток от деления параметра N1 на N2. Результат деления возвращается через имя функции, а остаток через параметр Ost.

Для того чтобы обеспечить возвращение вычисленного внутри функции остатка, соответствующий параметр функции определен как указатель (int *Ost). При вызове функции в качестве аргумента для этого параметра был использован адрес переменной &O, а не само значение переменной O. При вычислении остатка внутри функции выполняется разыменование параметра Ost (*Ost - обращение по адресу, хранящемуся в параметре Ost), и вычисленный остаток записывается по этому адресу (то есть в переменную O). Таким образом, значение переменной O изменяется.

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

Таким образом, для использования передачи данных с помощью указателей необходимо обязательно выполнить три следующих пункта:

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

2. При вызове функции на месте параметров-указателей необходимо использовать адрес аргумента, а не сам аргумент.

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

Несколько проще обстоит дело с передачей массивов, так как переменные типа массив сами являются указателями на первый элемент массива. В связи с этим отпадает необходимость в выполнении пунктов 2 и 3 из перечисленных выше. Рассмотрим пример:

void ReadArr ( int *P, int n)

{

for (int I = 0; I < n; ++I)

cin >> P[I];

}

void WriteArr ( int Arr[], int n)

{

for (int I = 0; I < n; ++I)

cout << Arr[I] << “ “;

cout << endl;

}

int main()

{

const n = 10;

int A[n];

ReadArr (A, n);

WriteArr (A, n);

return 0;

}

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

Во второй функции параметр для передачи массива определен как массив из элементов типа int, но это одновременно и указатель на первый элемент массива.

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

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

Кода осуществляется вызов этих функций, определять адрес аргумента для параметра–массива необходимости нет, так как переменная A сама является адресом первого элемента массива.

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

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

void StringProc(char * S, int L)

или

void StringProc(char S[], int L)

Этот способ передачи данных часто называют передачей данных по адресу.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]