Как сказал каждый ответ (пока): чтобы избежать ошибок, объявите функцию перед ее использованием, и, как указывает Крис Джестер-Янг, используйте стандартные механизмы для доступа к аргументам variadic или испытайте боль от неопределенного поведения .
В этом примере дело не в том, что объявление перед использованием в этом случае требуется из-за типа возвращаемого значения функции, а скорее потому, что оно не соответствует сигнатуре по умолчанию для необъявленной функции: int u()
; то есть функция, принимающая неуказанные аргументы, возвращающая int
. В частности, фактическое определение int maw(int,int,...)
является несовместимым с , отличным от принятого объявления int maw()
, которое заставляет GCC сказать "конфликтующие типы для" maw ".
Ваш второй пример
int main() {
abc(); ..... return 0;
}
abc() { ....... }
работает, поскольку более позднее определение abc
не противоречит сигнатуре по умолчанию, принятой при его первом вызове. Но только потому, что это работает, это не делает его хорошей формой, потому что вы почти не получаете безопасность типов из сигнатуры по умолчанию.
Доступ к переменным аргументам (совпадающим с ...
) действительно должен осуществляться через стандартные механизмы stdarg.h
, если только вы не реализуете компилятор и не являетесь автором stdarg.h
этого компилятора. Например, на некоторых архитектурах эти аргументы могут даже не передаваться в стек, но они все равно могут быть найдены макросами из stdarg.h
.
Редактировать: Я перефразировал второй абзац, чтобы сказать, что я имел в виду по-другому, и я надеюсь, более ясным, способом.
Компилятору необходимо знать, что функция является переменной до первого вызова, потому что на некоторых архитектурах может потребоваться передать параметры переменной иначе, чем обычные параметры. Это особенно верно для некоторых архитектур RISK с окнами регистров, а также для тех, которые могут передавать первые 2 целых и первые 2 плавающих числа в регистрах, но должны помещать все переменные в стек, даже если в регистрах есть место.
Аналогичное объявление перед использованием существует для функций, которые не используют cdecl
соглашение о вызовах. Обычно вы сталкиваетесь с этим при связывании модулей, написанных на Pascal или FORTRAN, с модулями, написанными на C. Однако многие из функций Windows API, экспортируемых основными DLL, предполагают соглашение о вызовах с именем stdcall
, и если компилятор будет использовать * При вызове в стиле 1033 * программа зависнет (и вся машина в Windows 9x или более ранней версии).