strcat(s,” “);// додаємо пробіл після слова
}
cout<<”\nНовий рядок:\n”<< s;// виводимо результат
}
Приклад 2:
Програма яка підраховує скільки разів задане слово зустрічається у тексті файлу. Наприклад, у англійській поговірці “Don’t trouble trouble until trouble troubles you” слово “trouble” у чистому вигляді зустрічається 3 рази.
#include <fstream.h>
#include <string.h>
# include<ctype.h>
void main()
{const int len=81;
char word[len], line [len];// масиви для слова і рядка
cout<< “Введiть слово для пошуку:”; cin>> word;
int_lword=strlen(word);// визначення довжини слова
ifstream fin (“text.txt”, ios:: in | ios:: nocreate);
if(!fin) {cout<< “Помилка відкриття файлу.”<<endl;return 1;}
int count=0;
// поки не досягнуто нуль-символу
while(fin.getline(line, len))
{char *p=line;// вказівникові присвоєно адресу рядка
while(p=strstr(p,word))/* якщо слово знайдено
вказівник стає на позицію
початку слова у рядку*/
{ // адреса початку входження слова передається с
char * c=p;
p+=l_word;// перехід вказівника р через слово
// слово не на початку рядка
if(c!=line)
/* Чи є символ перед словом розділювачем? Інакше –
перейти до наступної ітерації */
if(!ispunct(*(c-2))&& isspace(*(c-1))) continue;
// Чи є символ після слова розділювачем?
if (ispunct(*p)|| isspace(*p)|| (*p==’\0’)) count ++;
}
}
cout << “Слово зустрічається в тексті ”<< count;
<<” разів”<<endl;
}
9. Функції користувача
9.1 ФУНКЦІЇ: ВИЗНАЧЕННЯ, ОПИС, ВИКЛИК
Функцію в С++ можна розглядати:
· як один з похідних типів даних (поряд з масивами і вказівниками);
· як мінімальний виконуваний модуль програми (підпрограму).
Всі функції мають однаковий формат визначення:
<тип><ім’я_функції>(<список_формальних_параметрів>)
<тіло_функції>,
де <тип> - тип результату, який повертається функцією; в разі, якщо функція не повертає ніякого значення, її специфікують типом void і називають “порожньою”. Найчастіше, це функції, які виводять на екран повідомлення чи виконують деякі зміни параметрів, проте не можуть передати певний результат іншим змінним при виклику.
<ім’я_функції> - або main для головної функції, або довільний ідентифікатор;
<список_формальних_параметрів> - або порожній ( ), або список, кожен елемент якого записується як:
<тип> <ім’я_формального_параметру>
Наприклад:
(int k )
(char i, char j, int z)
<тіло_функції> - це набір операторів, що виконуються у фігурних дужках {} при виклику функції. Тіло функції може бути складовим оператором або блоком. Визначення функцій не можуть бути вкладеними.
Для передачі результату з функції у функцію, що її викликала, використовується оператор return. Його можна використовувати у двох формах:
1) return 0; - завершує функцію, яка не повертає ніякого значення (тобто перед її іменем вказано тип void);
2) return <вираз>; - повертає значення виразу, тип виразу повинен співпадати з типом функції.
Приклад 1:
int op (char c, int x, int y)
{switch (c)
{case ‘+’ : return x+y;
case ‘-’ : return x-y;
case ‘*’ : return x*y;
case ‘/’ : return x/y;
default: cout<<“\nОперація не визначена!”;
return 0;
}
}
Приклад 2.
float cube(float d)
{return d*d*d;}
Після визначення функцію можна багаторазово використовувати у програмі для виконання однотипних дій над різними змінними.
Виклик функції має наступний вигляд:
<ім’я_функції>(<список фактичних параметрів>);
<список фактичних параметрів - або сигнатура, є переліком виразів, кількість яких дорівнює кількості формальних параметрів функції. Між формальними і фактичними параметрами повинна бути відповідність за типами. В якості фактичних параметрів можна використовувати змінні, визначені та ініціалізовані у програмі, з типами, що відповідають типам формальних параметрів. Якщо функція повертає значення, її виклик можна використати у правій частині операції присвоювання з метою передачі результату функції змінній, тип якої співпадає з типом функції, що викликається.
Наприклад:,
void main(){float s, f=0.55; s=cube(f);…}
В якості фактичних параметрів також можуть виступати явно задані константні значення:
Наприклад, виклик функції з прикладу 1 має наступний вигляд:
c = op ( ‘+’, 5 ,4 );
Оскільки визначення функцій не можуть міститися всередині блоків та складових операторів, тобто в інших функціях, у програмі вони можуть розміщуватися як до, так і після функції, яка їх викликає. В останньому випадку перед використанням функції у програмі необхідно розмістити її опис, або прототип, інакше виникатимуть проблеми. Компілятор передусім послідовно перевіряє коректність виклику та використання об¢єктів у програмі, при виявленні функції, яка не була описана чи визначена раніше, видасть повідомлення про помилку і вказівку про те, що функція повинна містити прототип. Те саме повідомлення Ви побачите на екрані, якщо використаєте у програмі функцію зі стандартних бібліотек і не під¢єднаєте заголовний файл, у якому вона описана. Прототип функції користувача багато в чому нагадує заголовок функції і має наступний формат:
<тип><ім’я_функції>(<список_формальних_параметрів>);
Головною відмінністю є наявність в кінці опису крапки з комою.
Так, прототипи функцій з прикладів 1 та 2 матимуть вигляд:
cube(float);
op(char c, int, int);
Прототип надає компіляторові інформацію про тип та ім¢я функції, а також про типи, кількість та порядок розміщення параметрів, які їй можна передавати. Зважаючи на це, імена формальних параметрів зазначати необов¢язково. Прототип являє собою зразок для здійснення синтаксичної перевірки компілятором.
9.2 ПЕРЕДАЧА МАСИВІВ У ФУНКЦІЮ
Якщо в якості параметру функції використовується позначення масиву, необхідно передати до функції його розмірність.
Приклад 3: Обчислення суми елементів масиву
int sum (int n, int a[] )
{ int i, s=0;
for( i=0; i<n; i++ )
s+=a[i];
return s;
}
void main()
{ int a[]={ 3, 5, 7, 9, 11, 13, 15 };
int s = sum( 7, a );
cout<<s;
}
Рядки в якості фактичних параметрів можуть визначатися або як одновимірні масиви типу char[], або як вказівники типу char*. На відміну від звичайних масивів, для рядків немає необхідності явно вказувати довжину рядка, оскільки будь-який рядок обмежується нуль-символом.
При передачі у функцію двовимірного масиву в якості параметру так само необхідно задавати кінцеві розміри масиву у заголовку функції. Робити це можна:
а) явним чином (а[3][4] тоді функція працюватиме з масивами лише заданої розмірності);
б) можна спробувати для квадратної матриці через додатковий параметр ввести розмірність (void matrix(double x[][], int n)), де n – порядок квадратної матриці, а double x[][] – спроба визначення двовимірного масиву зі заздалегідь невизначеними розмірами. В результаті на таку спробу компілятор відповість: Error…: Size of type is unknown or zero;
в) найзручнішим вважається спосіб представлення та передачі двовимірної матриці за допомогою допоміжних масивів вказівників на одновимірні масиви, якими в даному випадку виступають рядки двовимірного масиву. Всі дії виконуються в межах рядків, розмір яких передається у функцію за допомогою додаткового формального параметру або з використанням глобальних змінних.
Приклад 4.
# include<iostream.h>
//Функція транспонування квадратної матриці
void trans (int n, double*p[])
{double x;
for (int i=0; i<n-1; i++)
for(int j=i+1; j<n; j++)
{x=p[i][j];
p[i][j]=p[j][i];
p[j][i]=x;
}
}
void main(){
// Задано масив для транспонування
double A[4][4]={11, 12, 13, 14
21, 22, 23, 24
31, 32, 33, 34
41, 42, 43, 44};
// Допоміжний двовимірний масив вказівників
double * ptr[]={(double*)&A[0], (double*)&A[1],
(double*)&A[2], (double*)&A[3]};
// Виклик функції
int n=4;
trans(n, ptr);
for(int i=0; i<n;i++)
{cout<<”\n Рядок ”<<(i+1)<<”:”;
for(int j=0; j<n; j++)
cout<<”\t”<< A[i][j];
}
}
Перевантаження полягає в тому, що функція з одним іменем по різному виконується і повертає значення різних типів при передачі до неї фактичних параметрів у різній кількості та з різними типами. Для забезпечення перевантаження функцій необхідно для одного імені функції визначити заголовок і набір операторів для всіх функцій, що пов’язані з ним.
Приклад 5:
#include <iostream.h>
int max_element ( int n, int a[])
// знаходить максимальний елемент для масиву типу int
{
int max=a[0];
for ( i=1; i<n; i++)
if (a[i]>max) max=a[i];
return max;
}
long max_element ( int n, long a[])
// знаходить максимальний елемент для масиву типу long
{
long max=a[0];
for ( i=1; i<n; i++)
if (a[i]>max) max=a[i];
return max;
}
double max_element ( int n, double a[])
// знаходить максимальний елемент для масиву типу double
{
double max=a[0];
for ( i=1; i<n; i++)
if (a[i]>max) max=a[i];
return max;
}
float max_element ( int n, float a[])
// знаходить максимальний елемент для масиву типу float
{
float max=a[0];
for ( i=1; i<n; i++)
if (a[i]>max) max=a[i];
return max;
}
void main ( )
{
int x[]={10, 20, 30, 40, 50, 60};
long y[]={12L, 44L, 22L, 37L,30L};
int m1=max_element(6, x );
long m2=max_element(5, y);
}
9.4 ФУНКЦІЇ ЗІ ЗМІННОЮ КІЛЬКІСТЮ ПАРАМЕТРІВ
У мовах С та С++ допускаються функції, кількість параметрів у яких при компіляції не визначена. Крім того, можуть бути невідомими і типи параметрів. Кількість і типи параметрів стають відомими лише в момент виклику функції, коли явно задається список фактичних параметрів. Формат заголовку функції зі змінним переліком параметрів має вигляд:
<тип> <ім’я> (<специфікація_формальних_параметрів, …>)
Проте, кожна функція зі змінним переліком параметрів повинна мати механізм визначення їх кількості або типу. Для цього використовують два основні підходи:
1) Передача у функцію значення реальної кількості фактичних параметрів за допомогою одного або декількох обов’язкових параметрів;
2) Додавання до списку фактичних параметрів спеціального параметру-індикатора з унікальним значенням, яке буде сигналізувати про кінець списку.
Підхід (1) демонструє програма у прикладі 6, яка містить функцію зі змінним списком параметрів, перший з яких визначає кількість фактичних параметрів, що використовуються при викликанні функції: