Конфликт между неявным объявлением и функцией varargs - PullRequest
1 голос
/ 14 апреля 2009

Может ли кто-нибудь подтвердить, что стандарт говорит о типе возврата по умолчанию функции vararg. Я компилировал следующий код:

int main()
{
    maw(32,3,95,38,20,15);
    return 0;
}

int maw(int a,int b,...)
{
    int *p=&b,i=0;
    while(i++<a)
       printf("\t%d",*p++);
    return 0;
}

но выдало ошибку:

foo.c:9: error: conflicting types for ‘maw’
foo.c:10: note: a parameter list with an ellipsis can’t match an empty parameter name list declaration
foo.c:4: error: previous implicit declaration of ‘maw’ was here

но если во время определения пасти, если я упомяну это как void maw(int a, int b, ...), компиляция вполне подойдет.

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

Что стандарт говорит по этому поводу? Кто-нибудь может подтвердить это для меня?


Я знаю, что если я просто стандартизирую код, он будет работать отлично. Я сделал это. С этим проблем нет.

Моя точка зрения: в чем проблема с этим кодом? Это должно было работать отлично. Разве такой код не запускается без ошибок?

int main()
{

abc();
.....
return 0;
}

abc()
{
.......
}

Проблема возникает, когда используется функция переменного аргумента. Я знаю, что стандарты говорят об объявлении функции по умолчанию.

Ответы [ 7 ]

11 голосов
/ 14 апреля 2009

Остальные ответы правильны, если поставить первым прототип функции.

Кроме того, чтобы быть переносимым, не обращайтесь к аргументам ..., используя прямой указатель на последний (не vararg) аргумент, потому что тогда вам нужно заботиться о выравнивании и направлении стека вашей целевой платформы. Вместо этого используйте макросы <stdarg.h> и va_* для работы с varargs.

4 голосов
/ 14 апреля 2009

Объявите maw () перед main () или создайте прототип перед main (). Функции возвращают типы, которые вы им присваиваете, независимо от аргументов (переменные или нет).

2 голосов
/ 14 апреля 2009

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

int maw(int a, int b, ...);

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

1 голос
/ 14 апреля 2009

Как сказал каждый ответ (пока): чтобы избежать ошибок, объявите функцию перед ее использованием, и, как указывает Крис Джестер-Янг, используйте стандартные механизмы для доступа к аргументам 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 или более ранней версии).

1 голос
/ 14 апреля 2009

Это не имеет никакого отношения к типу возвращаемого значения - это тот факт, что вы вообще его объявили. Поместите объявление:

int maw(int a,int b,...);

перед использованием функции в main ()

0 голосов
/ 30 июля 2013

Вызов неявно объявленной функции, которая фактически принимает переменное число аргументов, имеет неопределенное поведение.

Я не увидел ответа, который фактически цитировал соответствующие части стандарта. Для неявного определения функции это разрешено только в C89 и C90.

Из С89, & 3.3 ;.2, & 4;

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

extern int  identifier();
появился.

В том же разделе, & para; 6, говорится, что вызов неявно объявленной функции, которая позже объявляется с переменным числом аргументов, вызывает неопределенное поведение.

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

0 голосов
/ 14 апреля 2009

Добавить объявление

int maw(int a,int b, ...);

до вашей main() функции.

В настоящее время вызов, который вы делаете в вашей функции main(), неявно определяет maw(), то есть компилятор "догадывается", какой тип возвращаемого значения maw() основан на информации, которую он дает в main(). Если вы объявите maw() до его вызова, у вас не возникнет этой проблемы.

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