Тема 8 Функції та їх  використання

Лекція 23

 

Функції

Функція — це іменована логічно завершена сукупність ого­лошень і операторів, призначених для виконання певної задачі.

Програма мовою С містить одну або декілька функцій, кожна з яких повинна бути оголошена та визначена до її пер­шого використання. Оголошення функції (прототип, заголовок) задає ім’я функції, тип значення, що повертає функція (якщо воно є), а також імена та типи аргументів, які можуть переда­ватися як у функцію, так і з неї. Визначення функції — це за­дання способу виконання операцій.

Слід нагадати, що серед функцій програми повинна бути одна з ім’ям main (головна функція), яка може знаходитися в будь−якому місці програми. Ця функція виконується завжди першою і закінчується останньою.

Усі функції мають однакову структуру визначення у вигляді:

[тип результату] ім'я функції ([список формальних аргументів])

{ // тіло функції

опис даних;

оператори;

[return] [вираз];

};

де тип результату — будь−який базовий або раніше описаний тип значення (за винятком масиву і функції), що повертається функцією (необов’язковий параметр). За відсутності цього па­раметра тип результату за замовчуванням буде цілий (int). Він також може бути описаний ключовим словом (void), тоді функ­ція не повертає ніякого значення. Якщо результат повертаєть­ся функцією, то в тілі функції є необхідним оператор return вираз;, де вираз формує значення, що співпадає з типом ре­зультату;

 ім’я функції — ідентифікатор функції, за яким завжди знахо­диться пара круглих дужок «( )», де записуються формальні ар­гументи. Фактично ім’я функції — це особливий вид покажчика на функцію, його значенням є адреса початку входу у функцію;

список формальних аргументів — визначає кількість, тип і порядок проходження переданих у функцію вхідних аргумен­тів, які розділяються комою («,»). У випадку, коли параметри відсутні, дужки залишаються порожніми або містять ключове слово (void). Формальні параметри функції локалізовані в ній і недоступні для будь−яких інших функцій.

Список формальних аргументів має такий вигляд:

([const] тип 1 [параметр 1], [const] тип 2 [параметр 2], . . .)

У списку формальних аргументів для кожного парамет­ра треба вказати його тип (не можна групувати параметри одного типу, вказавши їх тип один раз).

Тіло функції може складатися з описів змінних і операто­рів. Змінні, що використовуються при виконанні функції, мо­жуть бути глобальні і локальні. Змінні, що описані (визначені) за межами функції, називають глобальними. За допомогою глобальних параметрів можна передавати дані у функцію, не включаючи ці змінні до складу формальних параметрів. У тілі функції їх можна змінювати і потім отримані значення пере­давати в інші функції.

Змінні, що описані у тілі функції, називаються локальними або автоматичними. Вони існують тільки під час роботи функ­ції, а після реалізації функції система видаляє локальні змінні і звільняє пам’ять. Тобто між викликами функції вміст локаль­них змінних знищується, тому ініціювання локальних змінних треба робити щоразу під час виклику функції. За необхідності збереження цих значень, їх треба описати як статичні за допо­могою службового слова static, наприклад:

static int

х, у;

або static float р = 3.25;.

 Статична змінна схожа на глобальну, але діє тільки у тій функції, в якій вона оголошена.

На початку програми можна не описувати всю функцію, а записати тільки прототип. Запис прототипу може містити тіль­ки перелік типів формальних параметрів без імен, а наприкін­ці прототипу завжди ставиться символ “;», тоді як у описі (ви­значенні) функції цей символ після заголовка не присутній.

Механізм передачі параметрів є основним засобом обміну ін­формацією між функцією, що викликається, та функцією, яка викликає. Параметри, котрі зазначаються у заголовку опису функції, як відомо, називаються формальними, а параметри, які записані у операторах виклику функції — фактичними. Наведе­мо приклад фрагмента програми з використанням функцій:

double sqr (double); //−−−−−−−−−−−−−− прототип функції sqr()

main( ) //−−−−−−−−−−−−−−−−−− головна функція

{ //−−−−−−−−−−−−−−−− виклик функції sqr()

cout << "Квадрат числа=" << sqr (10) << endl;

}

double sqr (double p) //−−−−−−−−−−−−−−−−−−−−−− функція sqr()

{ return p*p; } //−−−−−−−−−−−−−− повернення по значенню

 

У результаті виконання програми буде виведено:

Квадрат числа = 100

Функція завжди має бути визначена або оголошена до її виклику. При оголошенні, визначенні та виклику тієї самої функції типи та послідовність параметрів повинні співпа­дати. На імена параметрів обмежень на відповідність не існує, оскільки функцію можна викликати з різними аргументами, а в прототипах імена ігноруються компілятором (вони необхідні тільки для покращення читання програми). Тип значення, що повертає функція, та типи параметрів спільно визначають тип функції.

У найпростішому випадку при виклику функції слід вка­зати її ім’я, за яким у круглих дужках через кому – перелічити імена аргументів, що передаються. Виклик функції може здійс­нюватися у будь−якому місці програми, де за синтаксисом до­зволяється вираз того типу, що формує функція. Якщо тип значення, що повертає функція не void, вона може входити до складу виразів або, у поодинокому випадку, розташовуватись у правій частині оператора присвоювання.

У мові С  визначено декілька способів передачі парамет­рів і повернення результату обчислень функцій, серед них най­більш широке використання набули:

·         виклик функції з передачею параметрів за допомогою формальних аргументів−значень;

·         виклик функції з передачею адрес за допомогою параметрів−покажчиків;

·         виклик функцій з використанням посилань, коли доступ до переданих параметрів забезпечується за допомогою альтер­нативного імені (синоніма);

·         виклик функцій з передачею даних за допомогою гло­бальних змінних;

·         виклик функцій з застосуванням параметрів, що задані за замовчуванням, при цьому можна використовувати або всі аргументи, або їх частину.

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

void fun (intр) //−−−−−−−−−−− функція fun()

{++p;

cout << " p = " << p << endl; }

void main ( ) //−−−−−−−−−−− головна функція

{ int x = 10;

fun (x); //−−−−−−−−−−− виклик функції

cout << "x = " << x << endl; }

 Результат роботи цього фрагмента програми:

р=11, х=10,

оскільки для виклику функції fun(x) до неї передається копія значення, що дорівнює 10. Всередині функції значення копії змінної збільшується на 1, тобто (++р), і тому виводиться

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

При цьому способі для звертання до функції достатньо на­писати її ім’я, а в дужках — значення або перелік фактичних аргументів. Фактичні аргументи повинні бути записані в тій же послідовності, що і формальні, і мати відповідний тип (крім аргументів за замовчуванням і перевантажених функцій).

Якщо формальними аргументами функції є параметри−значення і в ній не використовуються глобальні змінні, функція може передати у викликаючу її програму лише одне значення, що записується в операторі return. Це значення передається в місце виклику функції. Достроковий вихід з функції мож­на також організувати з використанням оператора return.

Виклик функції з використанням покажчиків забезпечує передачу до функції не значень параметрів, а їх адреси, тому можливо міняти значення цих змінних усередині функції і пе­редавати за її межі (в інші функції).

У цьому випадку для виклику функції у списку формаль­них параметрів необхідно записати адресу того параметра, який слід змінити, тоді відповідний формальний параметр мати­ме тип покажчика на цей параметр. Усередині функції здійс­нюється розіменування параметра−покажчика та виконання необхідних дій. Програма з використанням виклику функції з пе­редачею адрес за допомогою параметрів−покажчиків може мати вигляд:

// використання параметра покажчика

void fun2 (int *p)

{ ++*p;

cout << "*p = " << *p << endl; }

void main ( )

{ int x = 10;

fun2 (&x); // виклик функції

cout << "x = " << x << endl; 

} 

У результаті буде виведена інформація:

*p = 11 і x = 11 .

Виклик функції з використанням параметра−посилання здійснює передачу до функції не самої змінної, а тільки поси­лання на неї. У цьому випадку забезпечується доступ до пере­даного параметра за допомогою визначення його альтернатив­ного імені, бо, як відомо, посилання є синонімом імені пара­метра. Тоді всі дії, що відбуваються над посиланням, є діями над самою змінною. Такий спосіб передачі параметрів і повернення результату передбачає запис у списку фактичних пара­метрів імені змінної, а у списку формальних — параметрів−посилань.

Наприклад:

//−−−−−−−−−−−−−− використання параметра−посилання

void fun (int &p) //−−−−−−−−−− функція fun()

{ ++p;

cout << "p = " << p << endl; }

void main ( )

{ int x = 10;

fun (x); //−−−−−−−−−− виклик функції fun()

cout << "x=" << x << endl;

}

Одержимо результат попереднього фрагмента, тобто

р = 11 і х=11.

При застосуванні вказаних вище параметрів у функцію пе­редаються не значення змінних, а їхні адреси, тому можливо міняти значення цих змінних усередині функції і передавати за її межі (в інші функції). Коли необхідно, щоб деякі параметри не змінювали свої значення всередині функції, їх слід оголосити як параметри−константи, використовуючи модифікатор const.

Використовувати глобальні змінні для передачі даних між функціями дуже легко, оскільки вони видимі в усіх функціях, де описані локальні змінні з тими ж іменами. Але такий спосіб не є поширеним, тому що ускладнює налагодження програми та перешкоджає розташуванню функції у бібліотеці загального використання. Слід прагнути, щоб функції були максимально незалежними, а їхній інтерфейс повністю визначався прототипом функції. Наведемо приклад використання глобальних змінних:

#include < iostream.h >

int a, b, с;             // глобальні параметри

sum ( );                 //−−−−−−−−−−− прототип функції

main ( )                //−−−−−−−−−−− головна функція

{ cin >> а >>b;

sum();                //−−−−−−−−−−− виклик sum()

cout << с << endl;

}

sum( )                   //−−−−−−−−−−− функція sum()

{ с = а + b; }

 

Масиви як параметри функцій

Аргументами (параметрами) функцій можуть бути не тіль­ки змінні, але й масиви. У цьому випадку можна використову­вати як масиви фіксованого розміру, так і невизначеного (ма­сиви змінної довжини). При застосуванні масивів фіксованої довжини в заголовку функції в списку формальних аргументів указується тип масиву і його розмір, наприклад:

void sort (int mas[30]);

Якщо описується функція з масивом змінної довжини, то в за­головку вказується тип масиву невизначеного розміру і обов’яз­ково ще один параметр, за допомогою якого задається розмір­ність масиву, наприклад:

void sort (int mas[ ],intn);

Всі масиви у функції передаються за адресою (як покажчи­ки), тому у випадку зміни масивів у функції ці зміни зберігаються при поверненні у викликаючу функцію.

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

 Приклад.У масива хm1(10),m2(15),m3(12)визначити міні­мальний елемент та його індекс.

/*визначення мінімального значення кількох масивів,використання глобальної змінної*/

#include <iostream.h>

#include <conio.h>

int ind=0;//−−−−−−−−−−−−−−−−−−−−−−−−−−глобальна змінна

//−−−−−−−−−−−−−−−−−−−функція вводу елементів массива vvod()

vvod(float mas[ ],int n)

{ for (int і = 0; і <n ;i++)

{ cout<<"Введіть"<<і <<" елемент ";

сіn>>mas [і]; }

cout<<endl;

}

//−−−−−−−−−−−−−−−функція визначення минім. елемента —fmin()

float fmin(float mas[ ],int n)

{ ind = 0; float min =mas[0];

for (int і = 1; і < n; i++)

if (mas[i] <min)

{ min = mas[i]; ind = i;

}

return min;

}

//−−−−−−−−−−−−−−−головна функція

main()

{

float ml[5], m2[6], m3[7];

cout« "*****Введіть масив m1\n";

vvod(ml,5);//виклик функції vvod()— ввід масива m1[]

//виклик функції fmin()

cout<<"min1 = "<<fmin (m1,5);

cout<<" ind = "<<ind<<endl;

cout<<"*****Введіть масив m2\n";

vvod(m2,6);//ввід масива m2[]

cout<<"min2 = "<<fmin (m2,6)<<endl;

cout<<" ind = "<<ind<<endl;

cout<<"*****Введіть масив m3\n";

vvod(m3,7);//ввід масива т3[]

cout<<"min3="fmin(m3,7)<<endl;

cout<<"ind="<<ind<<endl;

getch();

}

Програма, крім головної функції main(),має також функ­цію vvod()введення елементів деякого формального масиву та функцію fmin() визначення мінімального елемента цього маси­ву. У головній функції здійснюється виклик функцій для роз­в’язання необхідних обчислень кожного конкретного масиву, для передачі параметрів використовується глобальна змінна.

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

//використання масивів як параметрів функції

#include <iostream.h>

#include <string.h>

#include <conio.h>

void fun1 (char st[5]);//−−−−−−−−−прототип функції fun1()

main()

{

char p[5] ="файл"; fun1 (p);

cout <<"p = " <<p <<endl;//p="диск"

getch();

return 0;

}

void fun1 (char st[5])//−−−−−−−−−−функція fun1()

{

cout <<"p = " <<st <<endl;//р="файл"strcpy (st,"диск");

}

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

У заголовку функції під час роботи з багатовимірним ма­сивом фіксованого розміру, наприклад матриціmat(7,10),вка­зуються розмірності масиву, тобто:

void fun1(intmat[7][10]);

Якщо застосовується багатовимірний масив невизначеної довжини, то невизначеним може бути тільки один вимір роз­мірності, наприклад:

void fun2 (int mat[][10], int rows, int cob);

Масиви у функції можуть передаватися також за допомо­гою покажчиків. У цьому випадку функція повинна мати опис покажчика на той тип даних, елементами якого є масив, а у ви­кликаючій функції достатньо вказати лише ім’я масиву (як відомо, у мові С ім’я масиву є адресою цього масиву).