Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Конспект лекцій по С.doc
Скачиваний:
7
Добавлен:
16.11.2019
Размер:
393.73 Кб
Скачать

4.3 Передача параметрів

4.3. Передача параметрів значенням, особливості передачі параметрів у випадку указників, псевдонімів, псевдонімів сталих, псевдонімів указників, масивів, псевдонімів масивів; замовчувані значення параметрів; перетворення типів при передачі параметрів

Як ми побачили з попереднього розділу в С++ підтримується передача параметрів значенням. Власне це єдиний спосіб передачі параметрів, якщо не рахувати текстових підстановок в макровизначеннях. Передача параметрів значенням має багато переваг, головною з яких є її висока надійність. Підпрограма не має виходу за рамки відведеної їй пам’яті, а тому сфера її впливу добре локалізується. Разом з тим добре відомі проблеми, що виникають при передачі параметрів значенням, а саме проблеми зміни значень фактичних параметрів.

Ось приклад

void DoubleMyValue (short valueParam)

{

valueParam *= 2;

}

int main ()

{

short number = 10;

DoubleMyValue (number);

cout<<number<<’\n’;

}

10

В результаті виконання підпрограми значення формального параметру valueParam подвоїться, але це подвоєння не справить жодного впливу на фактичний параметр number. Складена в такий спосіб програма стає марною. Правда, в цьому конкретному випадку ситуацію легко поправити

p>Результат виконання програми:

Перед swap(): i: 10 j: 20

Після swap(): i: 10 j: 20

Перший вихід із проблемної ситуації пропонують указники.

Як і раніше параметри передаються значеннями, але значення ці тепер адреси.

Перед swap(): i: 10 j: 20

Після swap(): i: 20 j: 10

void rswap( int &x, int &y)

{

int z = x; x=y; y=z;

}

int main() {

int i = 10;

int j = 20;

cout<<"Перед swap():\ti: "<<i<<"\tj: "<<j<<endl;

rswap( i, j ) ;

cout<<"Після swap():\ti: "<<i<<"\tj: "<<j<<endl;

return 0;

}

void pswap( int *x, int *y)

{

int z = *x; *x=*y; *y=z;

cout<<"pswap: "<<&x<<" z:"<<&z<<endl;

}

void rswap( int &x, int &y)

{

int z = x; x=y; y=z;

cout<<"rswap: "<<&x<<" z:"<<&z<<endl;

}

void swap( int x, int y)

{

int z = x; x=y; y=z;

cout<<"rswap: "<<&x<<" z:"<<&z<<endl;

}

int main() {

int i = 10, &ri = i;

int j = 20;

cout<<"main: "<<hex<<&i<<" ri: "<<&ri<<endl;

cout<<"Перед swap():\ti: "<<i<<"\tj: "<<j<<endl;

swap( i, j ) ;

cout<<"Після swap():\ti: "<<i<<"\tj: "<<j<<endl;

pswap( &i, &j ) ;

cout<<"Після pswap():\ti: "<<i<<"\tj: "<<j<<endl;

rswap( i, j ) ;

cout<<"Після rswap():\ti: "<<i<<"\tj: "<<j<<endl;

return 0;

}

Оголошення int *&v;читається справа наліво: псевдонім указника

void ptrswap( int *&, int *& );

int main()

{

int i = 10;

int j = 20;

int *pi = &i;

int *pj = &j;

cout<<"Перед ptrswap():\tpi: "

<<*pi<<"\tpj: "<<*pj<<endl;

ptrswap( pi, pj ) ;

cout<<"Після ptrswap():\tpi: "

<<*pi<<"\tpj: "<<*pj<<endl;

return 0;

}

Перед ptrswap: pi: 10 pj: 20

Після ptrswap: pi: 20 pj: 10

У цьому прикладі 1000 разів копіюватиметься масив з 1000 чисел незалежно від того, у чому полягатиме обчислення функції calc, наприклад це могла б бути сума або середнє арифметичне.

Якщо ми замінимо сигнатуру, задавши параметр псевдонімом, уникнемо копіювання, але не будемо гарантовані від зміни параметру всередині функції

int calc( Huge &par);

void getArray( int[ 10 ] ) ;

і

void getPtr( int* );

Треба добре розуміти, що оскільки масив передається указником, він не копіюється. Порівняйте з попереднім прикладом, де передавався елемент масиву, що в свою чергу був визначений як структура.

void putValues( int[ ], int size );

int main()

{

int i, j[ 2] ;

putValues( &i, 1);

putValues( j , 2);

return 0;

}

void putValues( int (&arr)[10] ) ;

int main()

{

int i, j [ 2 ];

putValues(i);

// помилкака: аргумент не є масивом

putValues(j);

// помилка:

// аргумент не є масивом з 10 елементів типу int

return 0;

}

p>Додатковий параметр sz відповідає за розмірність. Перша перевірка з’ясовує, чи були указники попередньо встановлені. Те ж саме копіювання символьних масивів додаткового параметру розмірності не потребує, оскільки вичерпання масиву перевіряється нульовим кодом

void сopyString (char *source, char *dest)

{

while ( *source != ‘\0’ )

{

*dest = *source;

dest++;

source++;

}

*dest = ‘\0’;

}

або це ж саме компактним С-текстом

void CopyString (char *source, char *dest)

{

while (*dest++ = *source++);

}

Для функцій існує можливість визначати замовчувані значення аргументів. Замовчування визначаються або безпосередньо в реалізації функції, або при визначенні її прототипу. Останній спосіб видається більш доцільним.

void GenerateATone (short frequency = 440)

{

//Частота 440 відповідає ноті сі

};

Виклики можуть бути такими

GenerateATone (330);

GenerateATone ();

Для визначення замовчувань у багатомісних функціях існує одне обмеження: всі аргументи без початкових значень повинні передувати у списку аргументів усім аргументам з замовчуваними значеннями. Так прототип

void NormalDefaults (short x, short y=2, short z=3);

відповідає правилу, в той час як наступний — ні, оскільки перший аргумент має, а другий аргумент не має замовчування

void WillNotCompile (long time = 100L, short stack);

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

#include <iostream.h>

void MyFunc( short param1,

short param2 = 0,

short param3 = 0 );

int main()

{

MyFunc( 1 );

MyFunc( 1, 2 );

MyFunc( 1, 2, 3 );

return 0;

}

void MyFunc( short param1,

short param2,

short param3 )

{

cout << "MyFunc( " << param1

<< ", " << param2

<< ", " << param3

<< " )\n";

}

MyFunc( 1, 0, 0 )

MyFunc( 1, 2, 0 )

MyFunc( 1, 2, 3 )