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

  Юрій  | 

  Оновл. 17 Чер 2020  | 

 100

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

Проблема

Подивіться на цей, здавалося б, невинний шматочок коду під назвою 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: З’ясуйте, які з наступних програм не пройдуть етап компіляції, які не пройдуть етап лінкінга, а які не пройдуть і те, і інше.

Завдання №4:

Завдання №5:

Завдання №6:

Відповіді

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

Відповідь №1

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

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

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

Відповідь №2

Відповідь №3

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

Відповідь №4

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

Відповідь №5

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

Відповідь №6

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

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

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

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

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