2017-01-06 4 views
2

Согласно C Программирование (Deitel):Кто определяет имя функции с ошибкой? Компилятор или компоновщик?

Стандартные библиотечные функции, такие как Printf и зсапЕ не являются частью языка программирования C. Например, компилятор не может найти орфографическую ошибку в printf или scanf. Когда компилятор компилирует оператор printf, он просто предоставляет пространство в объектной программе для «вызова» функции библиотеки. Но компилятор не знает, где функции библиотеки - это компоновщик. Когда компоновщик работает, он находит функции библиотеки и вставляет правильные вызовы этим библиотечным функциям в объектной программе. Теперь объектная программа завершена и готова к выполнению. По этой причине связанная программа называется исполняемым. Если имя функции написано с ошибкой, - это компоновщик, который обнаружит ошибку, потому что она не сможет сопоставить имя в программе C с именем любой известной функции в библиотеках.

Эти заявления оставляют меня сомнительными из-за существования файла заголовка. Эти файлы включаются во время фазы предварительной обработки, перед компиляцией и, как я прочитал, используются компилятором.

Так что, если я пишу print вместо printf, как не может компилятор увидеть, что нет функции, объявленной с этим именем, и выбросить ошибку?

Если это так, как говорится в книге, почему я могу объявить функцию в заголовочных файлах, если компилятор их не наблюдает?

+0

Компилятор может видеть, что функция не объявлена ​​с этим именем и не выдает ошибку. Это зависит от параметров компилятора и версии C. – chux

+0

Современный компилятор должен хотя бы дать предупреждение во время компиляции. Но некоторые компиляторы делают предположения о отсутствующем объявлении функции, и компоновщик затем найдет ошибку. Включите все предупреждения компилятора, потому что, если функция существует, но не была объявлена, эти допущения могут быть неправильными. –

+0

Обратите внимание, что некоторые опечатки заканчиваются тем, что вызывали другую объявленную функцию из той, которая была предназначена. Иногда компилятор может заметить это - если параметры функции в вызове не совпадают с прототипом. Иногда ни компилятор, ни компоновщик не могут это заметить - если параметры функции в вызове совпадают с прототипом, но это просто неправильная функция. –

ответ

4

Так что, если я пишу печать вместо printf, как не может компилятор увидеть, что нет функции, объявленной с этим именем, и выбросить ошибку?

Вы правы. Если вы сделали опечатку в любом имени функции, любой современный компилятор должен жаловаться на это. Например, GCC жалуется на следующий код:

$ cat test.c 
int main(void) 
{ 
    unknown(); 
    return 0; 
} 
$ gcc -c -Wall -Wextra -std=c11 -pedantic-errors test.c 
test.c: In function ‘main’: 
test.c:3:5: error: implicit declaration of function ‘unknown’ [-Wimplicit-function-declaration] 
    unknown(); 
    ^

Однако в предварительном C99 эпоху языка C, любая функция, декларация не рассматривается компилятором, он будет считать, функция возвращает int. Итак, если вы компилируете в режиме pre-C99, компилятор не должен предупреждать об этом.

К счастью, этот implicit int rule был удален с языка C с C99, и компилятор должен предоставить для него диагностику в современном C (> = C99).

Но если вы предоставляете только заявление или прототип функции:

$ cat test.c 
int unknown(void); /* function prototype */ 

int main(void) 
{ 
    unknown(); 
    return 0; 
} 
$ gcc -c -Wall -Wextra -std=c89 -std=c11 test.c 
$ 

(Примечание: Я использовал -c флаг просто компилировать без ссылки, но если вы не используете -c затем компиляции & связывания будет сделано за один шаг, и ошибка все равно будет получена от компоновщика).

Нет проблем, несмотря на то, что у вас нет определения для unknown() в любом месте. Это связано с тем, что компилятор предполагает, что unknown() был определен в другом месте и только тогда, когда компоновщик пытается разрешить символ unknown, он будет жаловаться, если он не сможет найти определение для unknown().

Как правило, заголовочный файл (ы) содержат только необходимые декларации или прототипы (я указал прототип для unknown непосредственно в самом файле в приведенном выше примере - это также может быть сделано через файл заголовка) и обычно не фактическое определение. Следовательно, автор прав в том смысле, что компоновщик является тем, который указывает на ошибку.

4

Так что, если я пишу печать вместо printf, как компилятор не может видеть, что нет функции, объявленной с этим именем, и выкинуть ошибку?

Компилятор может видеть, что нет никакого объявления в области видимости идентификатора, обозначающего функцию. Большинство из них выдадут предупреждение при таких обстоятельствах, а некоторые из них выдадут ошибку или могут быть настроены для этого.

Но это не то же самое, что компилятор, обнаруживающий, что функция не существует. Это компилятор, обнаруживающий, что имя функции не было объявлено. Компилятор будет демонстрировать такое же поведение, если вы правильно произнесете имя функции, но не включите в нее предварительное объявление.

Кроме того, C90 и предварительная стандартизация C разрешенные вызовы функций без предварительного объявления. Такие вызовы не соответствуют C99 или новее, но большинство компиляторов по-прежнему принимают их (обычно с предупреждением) для целей совместимости.

Если это так, как говорится в книге, то почему я могу объявить функцию в заголовочных файлах, если компилятор их не наблюдает?

Компилятор действительно их видит и использует декларации. Более того, он полагается на прототип, если декларация обеспечивает его, для выполнения соответствующих преобразований аргументов и возвращаемых значений при вызове функции. Более того, если вы используете функции, типы аргументов которых изменяются по умолчанию, то ваши вызовы к таким функциям несоответствуют, если в точке вызова прототип не находится в области видимости. Неопределенные результаты поведения.

+0

Обратите внимание, что GCC 5.1.0 и по умолчанию используется для режима C11 (тогда как GCC 4.x и ниже по умолчанию для режима C90), поэтому современные версии GCC (6.3.0 являются текущими) сообщают об отсутствии деклараций автоматически. –

+0

@JonathanLeffler. Уместно, что компиляторы, используемые в реальном мире, отличаются тем, предупреждают ли они по умолчанию о незаявленных функциях, но, насколько я могу судить, C90 (и C99) не отличаются от C11, требуя предварительного объявления идентификатора как обозначение указателя функции или функции до того, как этот идентификатор используется в качестве операнда в выражении вызова функции. Другими словами, я не понимаю, в чем разница, основанная на версии реализованного языкового стандарта. –

+1

C90 не требовал, чтобы функции были объявлены до их использования.Если компилятор столкнулся с идентификатором, за которым следует левая скобка '(' (скобка в Америке), и для этого идентификатора не было другой информации, тогда она считалась функцией, возвращающей 'int' (с неопределенным невариантным аргументом список). Это правило означает, что с GCC 4.x или ранее не жаловались на пропуски деклараций функций без дополнительных параметров, чтобы жаловаться. В GCC 5.x и более поздних случаях жалобы запускаются автоматически без дополнительных опций. из соответствующих вариантов. –