Урок №22. Прототип функції і Попереднє оголошення

  Юрій  | 

  Оновл. 4 Вер 2021  | 

 521

На цьому уроці ми розглянемо, що таке попереднє оголошення і прототип функції в мові С++.

Наявність проблеми

Подивіться на цей, здавалося б, невинний шматочок коду під назвою add.cpp:

Ви, напевно, очікуєте побачити приблизно наступний результат:

The sum of 3 and 4 is: 7

Але по факту ця програма навіть не скомпілюється. Причиною цьому є те, що компілятор читає код послідовно. Коли він зустрічає виклик функції add() в рядку №5 функції main(), то він навіть не знає, що таке add(), тому що ми цю функцію ще не визначили! Через це ми отримаємо наступну помилку:

add: идентификатор не найден

Щоб усунути цю проблему, ми повинні врахувати той факт, що компілятор не знає, що таке add(). Є два шляхи вирішення даної проблеми.

Рішення №1: Розмістити визначення функції add() вище її виклику (тобто, перед функцією main()):

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

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

Прототипи функцій і попереднє оголошення

Рішення №2: Використовувати попереднє оголошення.

Попереднє оголошення повідомляє компілятору про існування ідентифікатора ДО його фактичного визначення.

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

Ось прототип функції add():

А ось вищенаведена програма, але вже з прототипом функції в якості попереднього оголошення аdd():

Тепер, коли компілятор зустрічає виклик функції add() в функції main(), він знає, що це таке і де його шукати.

Варто відзначити, що в прототипах функцій можна і не вказувати імена параметрів. Наприклад, вищенаведений прототип ми можемо записати наступним чином:

Проте краще вказувати імена параметрів, щоб не плутатися зайвий раз.

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

Попередньо оголосили, але не визначили

Питання: «А що буде, якщо ми попередньо оголосимо функцію, але не запишемо її визначення?”. Однозначної відповіді немає. Якщо попереднє оголошення записано, але функція ніколи не викликається, то програма може запуститися без помилок. Однак, якщо попереднє оголошення записано, функція викликається, але її визначення немає, то ви отримаєте помилку на етапі лінкінгу: програма просто не зможе опрацювати виклик цієї функції.

Розглянемо наступну програму:

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

Оголошення vs. Визначення

У мові C++ ви часто будете чути слова “оголошення” і “визначення”. Що це таке?

Визначення фактично реалізує (спричиняє виділення пам’яті) ідентифікатор. Ось приклади визначень:

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

У мові C++ є правило “одного визначення”, яке складається з наступних трьох частин:

   Всередині файлу: функція, об’єкт, тип чи шаблон можуть мати тільки одне визначення.

   Всередині програми: об’єкт чи звичайна функція можуть мати тільки одне визначення.

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

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

Оголошення — це стейтмент, який повідомляє компілятор про існування ідентифікатора і його тип. Ось приклади оголошень:

Оголошення — це все, що необхідно для задоволення компілятора, але недостатньо для задоволення лінкера. Визначення — це те, що робить щасливим як компілятора, так і лінкера.

Тест

Завдання №1: У чому різниця між прототипом функції та її попереднім оголошенням?

Завдання №2: Запишіть прототип наступної функції:

Завдання №3: З’ясуйте, які з наступних програм не пройдуть етап компіляції, які не пройдуть етап лінкінга, а які не пройдуть і те, і інше.

Програма №1:

Програма №2:

Програма №3:

Програма №4:

Відповіді

Щоб переглянути відповідь, клікніть на неї мишкою.

Відповідь №1

Прототип функції — це стейтмент оголошення функції, який включає її ім’я, тип повернення і параметри. Тіло функції не записується.

Попереднє оголошення повідомляє компілятор про існування ідентифікатора до його фактичного визначення.

Для функцій прототип є попереднім оголошенням.

Відповідь №2

Відповідь №3

   Програма №1: Не скомпілюється. Компілятор скаржитиметься, що у виклику функції add() занадто багато аргументів.

   Програма №2: Не скомпілюється. Компілятор скаржитиметься, що виклик функції add() не може прийняти стільки аргументів.

   Програма №3: Провал на етапі лінкінгу. Функція аdd(), яка приймає два параметри, не була визначена (ми визначили функцію, яка приймає 3 параметри).

   Програма №4: Успішні компіляція та лінкінг. Виклик функції add() відповідає прототипу, який був оголошений і визначенню функції.

Оцінити статтю:

1 Зірка2 Зірки3 Зірки4 Зірки5 Зірок (11 оцінок, середня: 5,00 з 5)
Loading...

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

Ваш E-mail не буде опублікований. Обов'язкові поля відмічені *