Что произойдет, если я не включу файлы заголовков при запуске программы ac? Я знаю, что получаю предупреждения, но программы работают отлично.
Я знаю, что файлы заголовков содержат объявления функций. Поэтому, когда я их не включаю, как компилятор это понимает? Проверяет ли он все файлы заголовков?
Я знаю, что получаю предупреждения, но программы работают отлично.
Это неудачное наследие pre-ANSI C: язык не требует прототипов функций, поэтому стандарт C позволяет ему по сей день (как правило, можно получить предупреждение, чтобы найти функции, называемые без прототипа).
Когда вы вызываете функцию без прототипа, компилятор C делает предположения о вызываемой функции:
int
...
vararg stuff) Если функция, вызываемая без прототипа, соответствует этим предположениям, ваша программа будет работать правильно; в противном случае это неопределенное поведение.
До стандарта ANSI C 1989 года не было возможности объявить функцию и указать типы ее параметров. Вам просто нужно было быть очень осторожным, чтобы каждый вызов соответствовал вызываемой функции, без предупреждения от компилятора, если вы ошибаетесь (например, передаете int
в sqrt()
). При отсутствии видимого объявления любая функция, которую вы вызываете, должна была возвращать int
; это было правило «неявное int». Многие стандартные функции возвращают int
, поэтому вы часто можете уйти с отсутствием #include
.
Стандарт ANSI C 1989 года (который также, по существу, стандарт ISO C 1990 года) вводил прототипы, но не делал их обязательными (и они все еще не являются). Поэтому, если вы позвоните
int c = getchar();
он будет работать, потому что getchar()
возвращает int
.
В стандарте ISO ISO 1999 года было исключено неявное правило int
и было запрещено его использование (фактически нарушение ограничений ) для вызова функции без видимого объявления. Поэтому, если вы вызываете стандартную функцию без обязательного #include
, компилятор, соответствующий C99, должен выдать диагностику (которая может быть просто предупреждением). Объявления непропротеированных функций (те, которые не указывают типы аргументов) по-прежнему являются законными, но считаются устаревшими.
(В 2011 году стандарт ISO C не сильно изменился в этой конкретной области).
Но есть еще много кода, который был написан для компиляторов C90, и большинство современных компиляторов по-прежнему поддерживают старый стандарт.
Поэтому, если вы вызываете стандартную функцию без обязательного #include
, то, вероятно, произойдет следующее: (а) компилятор предупредит вас о отсутствующем объявлении, и (б) он предположит, что функция возвращает int
и принимает любое число и тип (ы) аргументов, которые вы фактически передали ему (также учитывающий продвижение типа, например, short
to int
и float
чтобы double
). Если звонок правильный, и если вы компилируете его, то ваш код, скорее всего, будет работать, но вы будете иметь еще одну вещь, о которой стоит беспокоиться, если это не удастся, возможно, по какой-то причине.
Вариадические функции, такие как printf
– это другое дело. Даже в C89 / C90 вызов printf
без видимого прототипа имел неопределенное поведение. Компилятор может использовать совершенно другое соглашение о вызовах для вариативных функций, поэтому printf("hello")
и puts("hello")
могут генерировать совершенно другой код. Но, опять же, для совместимости со старым кодом большинство компиляторов используют совместимое соглашение о вызове, поэтому, например, первая программа «hello world» в K & R1, вероятно, все еще будет компилироваться и запускаться.
Вы также можете написать свои собственные объявления для стандартных функций; компилятору все равно, видит ли он объявление в стандартном заголовке или в вашем собственном исходном файле. Но нет никакого смысла в этом. Декларации сильно изменились с одной версии стандарта на другую, а заголовки, которые пришли с вашей реализацией, должны быть правильными.
Итак, что на самом деле произойдет, если вы вызовете стандартную функцию без соответствующего #include
?
В типичной рабочей среде это не имеет значения, потому что при любой удаче ваша программа не переживет проверку кода.
В принципе, любой компилятор, который соответствует C99 или новее, может отклонить вашу программу с фатальным сообщением об ошибке. (gcc будет вести себя таким образом с -std=c99 -pedantic-errors
). На практике большинство компиляторов просто печатает предупреждение. Вызов, вероятно, будет работать, если функция вернет int
(или если вы проигнорируете результат), и если вы вернете все типы аргументов. Если вызов неверен, компилятор не сможет распечатать хорошую диагностику. Если функция не возвращает int
, компилятор, вероятно, предположит, что это так, и вы получите результаты мусора или даже сбой вашей программы.
Таким образом, вы можете изучить этот мой ответ, следить за чтением различных версий стандарта C, узнать, в какой редакции соответствует стандарт вашего компилятора, и определить обстоятельства, при которых вы можете спокойно опустить заголовок #include
– с риском, что вы будете что-то испортить в следующий раз, когда вы измените свою программу.
Или вы можете обратить внимание на предупреждения своего компилятора (которые вы включили с любыми параметрами командной строки), прочитать документацию для каждой вызываемой функции, добавить требуемый #include
s вверху каждого исходного файла, а не приходится беспокоиться об этом.
Прежде всего: просто включите их.
Если вы не используете компилятор, будет использоваться прототип по умолчанию для незаявленных функций, а именно:
int functionName(int argument);
Поэтому он будет компилироваться и связываться, если функции доступны. Но во время работы у вас будут проблемы.
Есть много вещей, которые вы не можете сделать, если не оставляете заголовки:
(Я надеюсь получить немного больше от комментариев, так как моя память не работает на этом …)
Для совместимости со старой программой компиляторы C могут компилировать вызывающие код функции, которые не были объявлены, при условии, что параметры и возвращаемое значение имеют тип int
. Что может случиться? Посмотрите, например, на этот вопрос: беспокоящий преобразование строки в длинную часть C. Я думаю, что это отличная иллюстрация проблем, с которыми вы можете столкнуться, если вы не включаете необходимые заголовки и поэтому не объявляете функции, которые используете. Что случилось с парнем, он пытался использовать atoll
без включения stdlib.h
где объявлен atoll
:
char s[30] = { "115" }; long long t = atoll(s); printf("Value is: %lld\n", t);
Удивительно, но это напечатано 0
, а не 115
, как и ожидалось! Зачем? Поскольку компилятор не видел объявления atoll
и предположил, что это возвращаемое значение является int
, и поэтому выбрала только часть значения, оставшегося на стеке функцией, другими словами, возвращаемое значение было усечено.
Вот почему из соображений рекомендуется скомпилировать ваш код с помощью функции -Wall
(все предупреждения -Wall
).