лекція 07

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

void power (int base, int exp)

if (exp<0) return; /* Чтобы не допустить возведения числа в отрицательную степень, здесь выполняется возврат в вызывающую функцию и игнорируется остальная часть функции. */

for (; exp; exp--) i = base * i;

cout <<"Результат равен: " <

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

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

Кожна функція, крім типу void. повертає якесь значення. Це значення явно задається за допомогою інструкції return. Іншими словами, будь-яку НЕ void -функцію можна використовувати в якості операнда в вираженні. Отже, кожне з наступних виразів допустимо в C ++.

if (max (х, у))> 100) cout <<"больше";

Незважаючи на те що все не void -Функції повертають значення, вони необов'язково повинні бути використані в програмі. Найпоширеніший питання щодо значень, що повертаються функціями, звучить так: "Оскільки функція повертає деяке значення, то хіба я не повинен (повинна) присвоїти це значення якої-небудь змінної?". Відповідь: ні, це не обов'язково. Якщо значення, що повертається функцією, не бере участі в привласненні, воно просто відкидається (втрачається).

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

using namespace std;

i = abs (-10); // рядок 1

cout <

Функція abs () повертає абсолютне значення свого целочисленного аргументу. Вона використовує заголовок . У рядку 1 значення, що повертається функцією abs (). присвоюється змінної i. У рядку 2 значення, що повертається функцією abs (). нічому не присвоюється, але використовується інструкцією cout. Нарешті, в рядку 3 значення, що повертається функцією abs (). втрачається, оскільки не присвоюється ніякої іншої змінної і не використовується як частина виразу.

Якщо функція, тип якої відмінний від типу void. завершується в результаті виявлення закриття фігурної дужки, то значення, яке вона повертає, не визначене (тобто невідомо). Через особливості формального синтаксису C ++ НЕ void-функция не зобов'язана виконувати інструкцію return. Це може статися в тому випадку, якщо кінець функції буде досягнутий до виявлення інструкції return. Але, оскільки функція оголошена як повертає значення, значення буде таки повернуто, навіть якщо це просто "сміття". У загальному випадку будь-яка створювана вами не void -функція повинна повертати значення за допомогою явно виконуваної інструкції return.

Вище згадувалося, що void -функція може мати кілька інструкцій return. Те ж саме відноситься і до функцій, які повертають значення. Наприклад, представлена ​​в наступній програмі функція find_substr () використовує дві інструкції return. які дозволяють спростити алгоритм її роботи. Ця функція виконує пошук заданого підрядка в заданому рядку. Вона повертає індекс першого виявленого входження заданого підрядка або значення -1. якщо задана підрядка не була знайдена. Наприклад, якщо в рядку "Я люблю C ++" необхідно відшукати подстроку "люблю". то функція find_substr () поверне число 2 (це може бути індекс символу "л" в рядку "Я люблю C ++").

using namespace std

int find_substr (char * sub, char * str);

index = find_substr ( "три", "один два три чотири");

cout <<"Индекс равен " <

// Функція повертає індекс шуканої підрядка або -1, якщо вона не була знайдена.

int find_substr (char * sub, char * str)

p = str [t]; // установка покажчиків

while (* p2 * P2 == * p)

/ * Якщо досягнуто кінця р2-рядка (тобто підрядка), то підрядок була знайдена. * /

if (! * p2) return t; // Повертаємо індекс підрядка.

return -1; // Підрядок була виявлена.

Результати виконання цієї програми такі.

Оскільки шукана підрядок існує в заданому рядку, виконується перша інструкція return. Як вправа змініть програму так, щоб нею виконувався пошук підрядка, яка не є частиною заданого рядка. У цьому випадку функція find_substr () повинна повернути значення -1 (завдяки другій інструкції return).

Функції, які не повертають значень (void-функції)

using namespace std;

void print_vertical (char * str);

int main (int argc, char * argv [])

void print_vertical (char * str)

cout <<*str++ <<'\n';

Оскільки print_vertical () оголошена як void -функція, її не можна використовувати в вираженні. Наприклад, така інструкція невірна і тому не скомпілюється.

х = print_vertical ( "Привіт!"); // помилка

Важливо! В перших версіях мови С не був передбачений тип void. Таким чином, в старих С-програмах функції, що не повертають значень, за замовчуванням мали тип int. Якщо вам доведеться зустрітися з такими функціями при перекладі старих С-програм "на рейки" C ++, просто оголосіть їх з використанням ключового слова void, зробивши їх void-функціями.

Функції, які повертають покажчики

Функції можуть повертати покажчики. Покажчики повертаються подібно значенням будь-яких інших типів даних і не створюють при цьому особливих проблем. Але, оскільки покажчик являє собою одне з найскладніших (або небезпечних) засобів мови C ++, має сенс присвятити йому окремий розділ.

Щоб повернути покажчик, функція повинна оголосити його тип як тип значення, що повертається. Ось як, наприклад, оголошується тип значення для функції f (). яка повинна повертати покажчик на ціле число.

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

У наступній програмі демонструється використання покажчика як тип значення, що повертається. Це - нова версія наведеної вище функції find_substr (). тільки тепер вона повертатися не індекс знайденого підрядка, а покажчик на неї. Якщо задана підрядок не знайдено, повертається нульовий покажчик.

// Нова версія функції find_substr ().

// яка повертає покажчик на подстроку.

using namespace std;

char * find_substr (char * sub, char * str);

substr = find_substr ( "три", "один два три чотири");

cout <<"Найденная подстрока: " <

for (t = 1; t<=n; t++) answer = answer* (t);

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

Рекурсивна функція factr () дещо складніше. Якщо вона викликається з аргументом, рівним 1. то відразу повертає значення 1. В іншому випадку вона повертає твір factr (n-1) * n. Для обчислення цього виразу викликається метод factr () з аргументом n-1. Цей процес повторюється до тих пір, поки аргумент не стане рівним 1. після чого викликані раніше методи почнуть повертати значення. Наприклад, при обчисленні факторіала числа 2 перше звернення до методу factr () призведе до другого зверненням до того ж методу, але з аргументом, рівним 1. Другий виклик методу factr () поверне значення 1. яке буде помножено на 2 (початкове значення параметра n ). Можливо, вам буде цікаво вставити в функцію factr () інструкції cout. щоб показати рівень кожного виклику і проміжні результати.

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

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

Основна перевага рекурсії полягає в тому, що деякі типи алгоритмів рекурсивно реалізуються простіше, ніж їх ітеративні еквіваленти. Наприклад, алгоритм сортування Quicksort досить важко реалізувати ітеративним способом. Крім того, деякі завдання (особливо ті, які пов'язані зі штучним інтелектом) просто створені для рекурсивних рішень. Нарешті, у деяких програмістів процес мислення організований так, що їм простіше думати рекурсивно, ніж итеративно.

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

Розглянемо ще один приклад рекурсивної функції. Функція reverse () використовує рекурсію для відображення свого строкового аргументу в зворотному порядку.

// Відображення рядка в зворотному порядку за допомогою рекурсії.

using namespace std;

void reverse (char * s);

char str [] = "Це тест";

Схожі статті