
C _Учебник_МОНУ
.pdf
Функції |
279 |
За обох способів до функції передається адреса параметра, який зазначено при викликанні. При передаванні вказівника всередині функції виникає потреба використовувати явно операції розіменування чи то отримання адреси, що робить менш зрозумілим текст коду функції і більш складним налагодження функції. За передавання за посиланням до функції передається посилання, тобто синонім імені, внаслідок чого всередині функції всі звертання до параметра неявно розіменовуються.
Отже, використовування посилань замість вказівників зоптимізовує читабельність програми, позбавляючи потреби застосовувати операції отримання адреси й розіменування. Використовування посилань замість передавання за значенням є більш ефективне, оскільки не потребує копіювання параметрів, а це є надто важливо при передаванні структур даних великого обсягу.
Розглянемо приклад функції з передаванням параметрів за посиланням: void duplicate(int& a, int& b, int& c)
{ |
a*=2; |
b*=2; c*=2; |
} |
|
|
int main |
() |
{int x=1, y=3, z=7; duplicate(x, y, z);
cout << "x=" << x << ", y=" << y << ", z=" << z;
return 0;
}
Результат:
x=2, y=6, z=14
У наведеному фрагменті функція duplicate() отримає три цілих параметри, кожний з яких збільшується вдвічі у тілі функції. Для того щоб ці зміни були відомі у точці виклику (в функції main()), всі параметри передаються за посиланнями. Фактичними параметрами є самі змінні x, y та z, а не копії їхніх значень. Функція підставляє змінну х замість формального параметра а, змінну у – замість формального параметра b, змінну z – замість формального параметра с:
void duplicate(int& a, int& b, int& c)
duplicate( x, |
y, |
z); |
Розглянемо детальніше різницю поміж передаванням параметра за значенням, за посиланням і за вказівником.
У поданому нижче фрагменті програми функція f має три параметри, один з яких передається за значенням, другий – за вказівником, а третій – за посиланням.
Тексти функції та її виклику в основній пр ограмі:
void f(int i, int* j, int& k); int main()
{int i=1, j = 2, k = 3; cout << "i j k\n";
Функції |
281 |
Звертаємо увагу, що фактичними параметрами при передаванні за посиланням і вказівником можуть бути лише змінні.
До функції swap_values2() параметри передаються за посиланням. Виклик цієї функції матиме більш природній вигляд:
a=3; b=5; swap_values2(a, b);
Якщо функція має повернути понад одне значення, можна передати їй за посиланням додаткові параметри, а у тілі функції їм буде присвоєно значення, які треба повернути. Наступна функція prevnext() отримає ціле число х і обчислюватиме попереднє prev та наступне next значення відносно введеного х:
void prevnext (int x, int& prev, int& next) { prev = x-1; next = x+1; }
При використовуванні масиву як параметра функції передається вказівник на його перший елемент, тобто масив завжди передається за адресою (див. п. 5.2.4). При цьому інформація про кількість елементів втрачається і слід передавати його розмірність через окремий параметр. Якщо розмірність масиву є константою, проблем не виникає, оскільки можна зазначити її при оголошенні формального параметра і як верхню межу циклів при опрацюванні масиву всередині функції. У разі передавання до функції масиву символів, тобто рядка, його фактичну довжину можна визначити за розташуванням нуль-символу і передавати кількість елементів немає потреби.
У поданому нижче фрагменті програми оголошується функція sum(), параметрами якої є масив і кількість його елементів. Ця функція обчислює суму елементів масиву.
int sum (int* mas, int n)
{// Варіанти: int sum(int mas[ ], int n) чи int sum(int mas[n]) (якщо n – константа). for(int i = 0, s = 0; i < n; i++) s += mas[i];
return s;
}
void main()
{int marks[] = {3, 4, 5, 4, 4};
cout << "Сума елементів масиву: "<< sum(marks, 5);
}
При передаванні до функції багатовимірних масивів усі розмірності, якщо вони є невідомі на етапі компіляції, мають передаватися як параметри (див. п. 5.3.4). Приміром, заголовок функції, яка обчислює суму елементів динамічного двовимірного масиву, може мати вигляд
int sum(const int **a, const int nr, const int nc);
Виклик цієї функції у програмі може бути таким:
cout << "Сума елементів a: " << sum((const int**)a,nstr,nstb);
Для звичайного статичного двовимірного масиву, коли обидві розмірності є відомі й є константами, заголовок функції матиме вигляд
int sum(int а[4][6]);


Функції |
283 |
void __fastcall TForm1::Button1Click (TObject *Sender) { double P; int i, n = Memo1->Lines->Count;
double *a = new double [n];
for(i=0; i<n; i++) a[i]=StrToFloat(Memo1->Lines->Strings[i]); P = dob(a, n);
if(P!=0) Edit1->Text=FloatToStr(P); else ShowMessage("Від’ємних нема");
}
Приклад 8.7 Увести матрицю дійсних чисел розмірності 4 3 й обчислити за допомогою функції мінімальний елемент матриці.
Тексти функції та її виклику в основній програмі: double matrix_min(double a[4][3])
{double min=a[0][0]; for(int i=0; i<4; i++) for(int j=0; j<3; j++) if(a[i][j]<min)
min=a[i][j]; return min;
}
void __fastcall TForm1::BitBtn1Click(TObject *Sender)
{double a[4][3], m; for(int i=0; i<4; i++) for(int j=0; j<3; j++)
a[i][j]=StrToFloat(StringGrid1->Cells[j][i]); m=matrix_min(a);
Edit1->Text=FloatToStr(m);
}
Приклад 8.8 Увести матрицю дійсних чисел розмірності 4 3 й обчислити за допомогою функції мінімальний елемент матриці та його індекси.
Розв‟язок. Реалізацію подібного завдання без створення функції вже було розглянуто в підрозд. 5.3. До того ж цей приклад є продовженням попереднього. Змінимо функцію в такий спосіб, щоб вона обчислювала і повертала три значення. У цьому разі слід передати до функції два додаткових параметри – ind_i та ind_j, – в які буде записано обидва індекси мінімального елемента. Ці параметри мають передаватися за посиланням (чи за вказівником), щоб у тілі функції можна було їх змінити і повернути ці зміни у точку виклику.
Тексти функції та її виклику в основній пр ограмі:
double matrix_min(double a[4][3], int& ind_i, int& ind_j)
{double min=a[0][0]; ind_i=0; ind_j=0; for(int i=0; i<4; i++)
for(int j=0; j<3; j++)
if(a[i][j]<min) { min=a[i][j]; ind_i=i; ind_j=j; } return min; }


Функції |
285 |
if(c[i] == 'ї' || c[i] == 'є') c[i] = c[i] - 16; if(c[i] == 'ґ') c[i] = 'Ґ';
if(c[i] == 'і') c[i] = 'І';
}
}
void __fastcall TForm1::Button1Click(TObject *Sender)
{char s[50];
strcpy(s, Edit1->Text.c_str()); toUp(s);
Edit2->Text=AnsiString(s);
}
Приклад 8.10 Заповнити матрицю розмірності M N (значення M та N увести) випадковими числами з проміжку [–15, 10) за допомогою функції.
Тексти функції та її виклику в основній пр ограмі:
void fill_random(int** a, int M, int N) { randomize();
for(int i=0; i<M; i++)
for(int j=0; j<N; j++) a[i][j]=random(25)-15;
}
void __fastcall TForm1::Button1Click(TObject *Sender)
{int M, N, i, j;
if(Edit1->Text=="" || Edit2->Text=="")
{ShowMessage("Введіть M та N"); return; } M=StrToInt(Edit1->Text); N=StrToInt(Edit2->Text); StringGrid1->RowCount=M; StringGrid1->ColCount=N;
int **a = new int* [M]; for(i=0; i<M; i++)
a[i]=new int [N]; fill_random(a, M, N);
for(i=0; i<M; i++) for(j=0; j<N; j++) StringGrid1->Cells[j][i]=
IntToStr(a[i][j]); for(i=0; i<M; i++) delete []a[i]; delete []a;
}
8.3 Параметри зі значеннями за замовчуванням
Щоб спростити виклик функції, в її заголовку можна задати значення параметрів за замовчуванням. Ці параметри мають бути останніми у списку й можуть опускатися при виклику функції. Якщо при виклику параметр є опущений, має бути опущено й усі параметри, що стоять за ним. Як значення параметрів за замовчуванням можуть використовуватися константи, глобальні змінні й
Функції |
287 |
Для роботи з функціями зі змінною кількістю аргументів використовуються величини va_list, va_start, va_arg і va_end, описані в заголовному файлі <stdarg.h>.
Тип va_list призначено для зберігання вказівника на черговий аргумент. Для роботи з функціями зі змінною кількістю аргументів використовуються декілька стандартних макросів (дещо подібних до функцій наборів ко-
манд).
Макрос va_start ініціює вказівник на черговий елемент, він встановлює аргумент arg_ptr на початок списку необов‟язкових параметрів і має вигляд функції з двома параметрами:
void va_start(arg_ptr, prav_param);
де параметр prav_param має бути останнім обов‟язковим параметром викликуваної функції, а вказівник arg_ptr має бути оголошено у списку змінних типу va_list у вигляді
va_list arg_pt;
Макрос va_start має бути використано до першого використання макросу va_arg. Макрос va_arg повертає значення чергового аргументу, кожен його виклик призводить до просування вказівника, який зберігається в va_list. Макрокоманда va_arg забезпечує доступ до поточного параметра викликуваної функції й теж має вигляд функції із двома параметрами:
type_arg va_arg (arg_ptr, type);
Ця макрокоманда бере значення типу type за адресою, заданою вказівником arg_ptr, збільшує значення вказівника arg_ptr на довжину використаного параметра (довжина type) і в такий спосіб параметр arg_ptr вказуватиме на наступний параметр викликуваної функції. Макрокоманда va_arg використається стільки разів, скільки треба для отримання всіх параметрів викликуваної функції.
Після перебирання аргументів, але до виходу з функції зі змінним числом аргументів, необхідно звернутися до макросу va_end. Він встановлює вказівник списку необов‟язкових параметрів на NULL.
Приклад 8.12 Увести послідовність цілих чисел і обчислити за допомогою функції зі змінною кількістю параметрів середнє значення цієї послідовності. За завершення послідовності слід вважати значення, яке дорівнює –1.
Розв‟язок. Оскільки у списку має бути принаймні один елемент, у функції буде один обов‟язковий параметр х, після якого слід поставити три крапки.
Тексти функції та її виклику в основній пр ограмі: float sred_znach (int x, ...)
{int i=0, j=0, sum=0; va_list uk_arg;
va_start(uk_arg, x); |
// Встановлення вказівника uk_arg на перший |
|
// необов‟язковий параметр. |
if(x!=-1) sum=x; |
// Перевірка наявності чисел у списку. |
else return 0; |
|