Прототипы функций
Компилятор читает вашу программу сверху вниз, как вы читаете книгу. Когда он доходит до вызова функции,он уже должен знать эту функцию: её имя, что она принимает и что возвращает. Поэтому мы до сих пор определялифункции выше main. В этой программе функция находится нижеmain:
#include <stdio.h>
int main(void) {
greet();
return 0;
}
void greet(void) {
printf("Привет!\n");
}Она не компилируется:
main.c:4:5: error: call to undeclared function 'greet'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
4 | greet();
| ^
main.c:8:6: error: conflicting types for 'greet'
8 | void greet(void) {
| ^
main.c:4:5: note: previous implicit declaration is here
4 | greet();
| ^
2 errors generated.Начнём с первой ошибки. В строке 4 main вызывает функцию greet, которая ещё необъявлена: ничего выше строки 4 не сообщило компилятору, что такая функция существует.Остальная часть строки говорит, что начиная с C99, версии C 1999 года, язык не разрешает неявные объявленияфункций. Очень старые компиляторы сами догадывались, как выглядит неизвестная функция, и часто ошибались.Вторая ошибка, conflicting types, лишь следует из первой: компилятор сам сделал догадку встроке 4 (об этом говорит note), а настоящая функция в строке 8 ей не соответствует. Когда выисправите первую ошибку, вторая тоже исчезнет.
Исправить можно двумя способами. Первый вы знаете: перенести всю функцию выше main. Второй —прототип функции выше main: это первая строка функции, в конце которой вместо теластоит точка с запятой:
#include <stdio.h>
void greet(void);
int main(void) {
greet();
return 0;
}
void greet(void) {
printf("Привет!\n");
}Эта программа выводит:
Привет!Прототип void greet(void); — это объявление функции. Он сообщает компилятору:есть функция greet, она ничего не принимает и ничего не возвращает, а её определение будет позже.Этого компилятору достаточно, чтобы проверить вызов. Прототип должен совпадать с определением: тот же типвозвращаемого значения, то же имя и те же скобки. И определение обязательно должно существовать: если естьпрототип, но нет определения, программа не запустится, а сообщение об ошибке закончится словамиundefined symbol: greet, то есть тела у greet нет.
Зачем писать прототипы? В длинной программе прототипы в начале работают как оглавление книги: вы видите всефункции сразу, а main может идти сразу за ними. А прототипами вы пользуетесь с первого урока, самитого не зная. Файл stdio.h — это заголовочный файл: в нём находятся прототипыprintf и других функций библиотеки, то есть набора готовых функций, который идётвместе с C. Строка #include <stdio.h> вставляет его содержимое в вашу программу, поэтомукомпилятор знает printf ещё до того, как вы её вызовете. Точно так же string.h объявляетstrlen, а ctype.h — toupper.
В этом курсе мы в основном будем писать функции целиком выше main, потому что так проще. Но когдавы увидите в программе прототип, вы будете знать, что это.
Задание
- 1
Программа не компилируется, потому что
mainвызываетprint_title, а эта функцияопределена ниже. Не переносите функцию. Вместо этого добавьте её прототип вышеmain. Тогда программавыведет заголовок иНажмите Старт, чтобы играть. - 2
Ниже функции
print_titleнапишите функциюvoid print_goodbye(void), которая выводитСпасибо за игру!, и добавьте её прототип вышеmain, под первым. - 3
В конце
main, передreturn 0;, вызовитеprint_goodbye. Программа выводит:КОСМОГОНКА ========== Нажмите Старт, чтобы играть. Спасибо за игру!