Давным-давно C не требовал предварительного объявления всех функций. Многие компиляторы все еще позволяют вам сойти с рук.
В файле A.c
, когда вы позвонили
a();
где a
была функция, которую компилятор никогда не видел прежде, компилятор предполагал, что объявление
extern int a();
был в сфере. То есть компилятор предполагал, что a
была функцией, принимающей неуказанные аргументы и возвращающей int
.
Или, что использовал , чтобы быть правилом. Этого правила больше нет в C, так что да, вы должны явно объявить все ваши функции перед тем, как вызывать их. Большинство современных компиляторов предупреждают вас, когда они применяют старое правило, и многие не желают применять правило вообще или, по крайней мере, нет, если вы не используете флаг опции, отличный от значения по умолчанию, запрашивающий их. Но, похоже, ваш компилятор все еще готов применить правило без предупреждения или ошибки. Это здорово, если вы компилируете кучу очень старого кода, но это не так здорово, если вы пытаетесь изучать современный C.
Теперь, в этом случае у вас есть дополнительная проблема, что фактическое определение функции a
в файле B.c
определяет его как возвращающее void
, а не int
, так что теоретически это неправильно , тоже. Но на практике ошибка неправильного определения (или неправильного вызова) void
- против int
- возвращающих функций является безвредной, которая не вызывает никаких реальных проблем. (Это все еще неправильно, хотя и стоит избегать.)
Я думаю, вы это знаете, но правильной настройкой было бы, чтобы файл A.c
выглядел так:
#include <stdio.h>
extern void a(void);
int main()
{
a();
}
или для создания файла B.h
, содержащего
extern void a(void);
и затем файл A.c
будет выглядеть так:
#include <stdio.h>
#include "B.h"
int main()
{
a();
}
(Обратите внимание, что я также изменил void main()
на int main()
, для корректности. Если вы используете старый компилятор, как вам кажется, вам также может понадобиться добавить строку return 0;
в конец main()
.)
Добавление. Вы также спрашивали об этом ключевом слове extern
. Это связано с различием между декларациями и определениями . Но это различие для функций несколько отличается от глобальных переменных.
Объявления объясняют, какой тип имеет что-то. Определения объясняют, что тип имеет что-то, и они дополнительно выделяют память для чего-то и предоставляют его начальное значение.
Это декларации:
extern int i;
int f(int);
extern int f2(int, double);
Это определения:
int i;
int i2 = 2;
int f(int x) { return 2 * x; }
int f2(int n, double x) { return n * x; }
Ключевое слово extern
прямо говорит: «Это декларация, определение где-то еще». Для глобальных переменных это имеет большое значение. Но для функций, когда вы говорите int f(int);
, компилятор может сказать, когда он находит ,
вместо {
, что это объявление (а не определение), поэтому ключевое слово extern
необязательно в объявления функций.
(Кроме того, функции всегда глобальны в C; локальных функций нет.)
См. Также раздел 4.2 и раздел 4.3 из эти примечания к курсу .