Оголошення та опис функцій у C++ Builder

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

I = 5 * F (X);

викликає функцію F з аргументом X, множить повернене нею значення на 5 і надає результат змінній I.

Допускається також виклик функції, що не використовує значення, що нею повертається. Наприклад:

F(X);

У цьому випадку значення, що повертається функцією, ігнорується. Функція описується наступним чином:

тип_повертаного_значення ім'я_функції(список_параметрів){
оператори тіла функції
}

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

Якщо тип значення, що повертається, не вказано, він за умовчанням вважається рівним int.

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

Список параметрів, що укладається в дужки, в найпростішому випадку (складніші форми завдання списку параметрів будуть розглянуті пізніше) являє собою список виду, що розділяється комами:

тип_параметра ідентифікатор_параметра

Наприклад, заголовок:

double FSum(double X1, double X2, int h)

оголошує функцію з ім’ям FSum, з трьома параметрами X1, X2 і А, у тому числі перші два мають тип double, а останній int. Тип результату, що повертається – double. Імена параметрів X1, X2 і локальні А, тобто. вони мають значення лише всередині цієї функції і не пов’язані з іменами аргументів, переданих під час виклику функції. Значення цих параметрів на початку виконання функції дорівнюють значенням аргументів на момент виклику функції.

Нижче наведено заголовок функції, яка не повертає жодного значення:

void SPrint(AnsiString S)

Вона приймає один параметр типу рядка і, наприклад, відображає його у якомусь вікні програми.

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

void F1(void)

або

void F1()

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

Роль порожнього списку параметрів функції С++ істотно відрізняється від аналогічного списку в мові С. У це означає, що всі перевірки аргументів відсутні (тобто виклик функції може передати будь-який аргумент, який потрібно). На С++ порожній список означає відсутність аргументів. Таким чином, програма на С, яка використовує цю особливість, може повідомити про синтаксичну помилку при компіляції С++.

Як правило (хоча формально не обов’язково), окрім опису функції до тексту програми включається також прототип функції її попереднє оголошення. Прототип є той же заголовок функції, але з точкою з комою “;” в кінці. Крім того, у прототипі не можна вказувати імена параметрів. Якщо ви все-таки вказуєте імена, їх областю дії є лише цей прототип функції. Ви можете використовувати ті ж ідентифікатори у будь-якому місці програми у будь-якій якості. Таким чином, вказівка імен параметрів у прототипі зазвичай переслідує тільки одну мету – документування програми, нагадування вам або людині, яка супроводжує програму, який параметр що саме позначає.

Приклади прототипів наведених вище заголовків функцій:

double FSum (double X1, double X2, int A);
void SPrint (AnsiString S);
void F1(void);

або

double FSum(double, double, int);
void SPrint (AnsiString);
void F1();

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

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

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

Зазвичай функції приймають вказану в прототипі кількість параметрів зазначених типів. Однак можуть бути функції, що приймають різну кількість параметрів (наприклад, бібліотечна функція printf) або параметри невизначених типів. У цьому випадку в прототипі замість невідомого числа параметрів або замість параметрів невідомого типу ставиться крапка “…”. Багатокрапка може розміщуватися тільки в кінці списку параметрів після відомого числа параметрів відомого типу або повністю замінювати список параметрів. Наприклад:

int prf(char* format, ...);

Функція з подібним прототипом приймає один параметр format типу char* (наприклад, рядок форматування) і довільне число параметрів довільного типу. Функція із прототипом:

void Fp(...);

може приймати довільну кількість параметрів довільного типу.

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

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

static void F(void);

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

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

Треба пам’ятати, що це оголошення у тілі функції носять локальний характер. Оголошені змінні доступні лише всередині цієї функції. Якщо їх ідентифікатори збігаються з ідентифікаторами якихось глобальних змінних модуля, ці зовнішні змінні стають невидимими і недоступними. У цих випадках отримати доступ до глобальної змінної можна, поставивши перед її ім’ям дві двокрапки “::”, тобто. застосувавши унарну операцію дозволу області дії.

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

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

double FSum(double X1,double X2, int A)
{
return A* (X1 + X2);
}

Нижче наведено приклад функції, яка не повертає жодного значення:

void SPrint(AnsiString S)
{
if (S != "")
ShowMessage(S);
}

Тут повернення з функції відбувається після досягнення фігурної дужки тіла функції, що закривається. Наведемо варіант тієї ж функції, що використовує оператор return:

void SPrint(AnsiString S)
{
if (S == "")
return;
ShowMessage(S);
}

Перервати виконання функції можна також генерацією якогось виключення. Найчастіше в цих цілях використовується процедура Abort, що генерує «мовчазне» виключення EAbort, не пов’язане з якимось повідомленням про помилку. Якщо у програмі не передбачено перехоплення цього виключення, то застосування функції Abort виводить керування відразу нагору зі всіх вкладених один одного викликів функцій.

Значення, що повертається функцією, може включати виклик якихось функцій. У тому числі функція може викликати і себе, тобто. допускається рекурсія. Як приклад наведемо функцію, що рекурсивно обчислює факторіал. Як відомо, значення факторіалу дорівнює n! = n (n-1) (n-2) … 1, причому вважається, що 1! = 1 та 0! = 1. Факторіал можна визначити за допомогою простого циклу for (і це, звичайно, простіше). Але можна факторіал обчислювати і за допомогою рекурентного співвідношення n! = n(n-1)! Для ілюстрації рекурсії скористаємося саме цим співвідношенням. Тоді функція factorial обчислення факторіалу може бути описана таким чином:

unsigned long factorial (unsigned long n)
{
if (S == "")
return;
ShowMessage(S);
}

Якщо значення параметра n дорівнює 0 або 1, то функція повертає значення 1. В іншому випадку функція множить поточне значення n на результат, який повертається викликом тієї ж функції factorial, але зі значенням параметра n, зменшеним на одиницю. Оскільки при кожному виклику значення параметра зменшується, рано чи пізно воно дорівнюватиме 1. Після цього ланцюжок рекурсивних викликів почне згортатися і зрештою поверне значення факторіалу.

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *