2014-12-05 2 views
2

Я могу скомпилировать и запустить следующий код с gcc 4.4.7.Почему передача дополнительных аргументов функции C не приводит к ошибке времени компиляции?

Файл: MC

#include <stdio.h> 

int main() 
{ 
    printf("%d\n", f(1, 2, 3)); 
} 

Файл: FC

int f(int a, int b) 
{ 
    return a + b; 
} 

Выход:

$ gcc m.c f.c && ./a.out 
$ 3 

Когда функция f() определяется в том же файле, компилятор выдает сообщение об ошибке, как ожидается. Я предполагаю, что компилятор не может обнаружить ошибочное использование функций между единицами компиляции. Но разве линкер не сможет его обнаружить? Указывает ли стандарт на ожидаемое поведение?

Обратите внимание: это отличается от объявления функции без каких-либо параметров, которая работает даже внутри одного файла. (Why does gcc allow arguments to be passed to a function defined to be with no arguments?).

Я использую GCC (GCC) 4.4.7 20120313 (Red Hat 4.4.7-11) и GNU л.д. версия 2.20.51.0.2-5.42.el6 20100205.

+0

C не проверен на тип или не набирается такого типа :-( –

+0

Для совместимости с программами на C, написанными до 1989 года. Однако код выше вызывает неопределенное поведение. "Если выражение, которое обозначает вызываемую функцию, имеет тип, который делает не включать прототип ... Если количество аргументов не равно числу параметров, поведение не определено ». –

+1

Чтобы избежать этой проблемы, добавьте заголовок' fh' с прототипом 'int f (int a, int b); 'и' # include' этот заголовок в 'fc' и в любом исходном файле, который вызывает' f'. И вызывать gcc с аргументами, которые предупреждают о вызовах необъявленных функций, либо '-std = cXX' для кода который не нуждается в расширениях GNU или '-std = gnuXX' для кода, который делает (где' XX' - '99' или' 11'). –

ответ

3

gcc в настоящее время компилируется с -std=gnu89 по умолчанию (не уверен в 5.x, но это верно для версий раньше). В C89 (GNU89 почти C89-надстройка), если функция вызывается без объявления быть видимым, то предполагается, что быть объявлена ​​как

extern int f(); 

функция с внешним связыванием, возвращающийся int и принимающим неустановленным (но фиксированное) число аргументов, поддерживаемых по умолчанию.

Это считалось ошибкой дизайна многими, отмеченными устаревшими в C89 и в конечном итоге удаляемыми на C99. Gcc дает предупреждение для неявных деклараций функций по умолчанию.

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

Используйте заголовочные файлы с прототипами, чтобы получить предупреждение.

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

+0

Кроме того, я предлагаю всегда компилировать с параметром -Wall, он будет освещать эту проблему, а также многие другие распространенные ошибки. – roy

1

EDIT:

I думаю, что я неправильно понял сначала:

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

OLD ОТВЕТ:

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

Вы даже можете планировать наличие неизвестного количества аргументов. Вот простой пример пользовательской функции стиля Printf для лесозаготовок:

void Debug_Message(uint32_t level, const char *format, ...) 
{ 
    char buffer[256]; 

    //check level and do stuff 

    va_list args; 
    va_start(args, format); 
    vsnprintf(buffer, sizeof(buffer), format, args); 

    //buffer now contains data as if we did an sprintf to it 
} 

Это будет называться так же, как Printf, может быть:

Debug_Message(1, "%d%d%d", 1, 2, 3); 

может быть:

Debug_Message(1, "%d", 1); 
+0

Вы описываете эффект (безвредный) ** во время выполнения **. OP спрашивает, почему нет ** ошибки компиляции **. –

+0

Упс, в этом случае я должен сказать, что это потому, что объявления функции нет. Когда нет объявления функции, gcc не знает, чего ожидать во время фазы компиляции, и до тех пор, пока она найдет функцию во время фазы связывания, все будет работать. Эта «ошибка» должна быть обнаружена во время фазы компиляции, поскольку это не является технической ошибкой на этапе связывания. @barakmanos – RobC

+0

Это тоже моя мысль, хотя, насколько я знаю, когда нет объявления, компилятор неявно принимает 'int f()' или 'int f (int)', ни один из которых не соответствует вызову ' f (1, 2, 3) '. Поэтому я все равно ожидаю ошибку компиляции (или предупреждение). При этом я не уверен, что стандарт «должен сказать об этом». В любом случае, если вы считаете, что это правильный ответ, возможно, вам следует обновить ваш. –

Смежные вопросы