隐式声明和varargs函数之间的冲突

时间:2009-04-14 08:03:48

标签: c

任何人都可以确认标准中有关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

但是如果在定义maw期间,如果我将其称为void maw(int a, int b, ...),那么编译就可以了。

我可以从中得出vararg函数的默认返回类型可能不是int

标准在这方面说了什么?任何人都可以帮我确认一下吗?


我知道如果我只是将代码标准化,它将完美运行。我已经做了。这没有问题。

我的观点是:这段代码有什么问题?它应该运行得很好。 像这样的代码不运行没有任何错误吗?

int main()
{

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

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

当使用变量参数函数时,问题就出现了。 我知道标准对于函数的默认declration的含义。

7 个答案:

答案 0 :(得分:11)

其他答案是正确的,关于首先放置函数原型。

此外,为了便于移植,不要通过使用指向最后一个(非vararg)参数的直接指针来访问...参数,因为那时你必须关心目标平台的对齐和堆栈方向。相反,请使用<stdarg.h>va_*宏来处理varargs。

答案 1 :(得分:4)

在main()之前声明maw(),或者在main()之前对其进行原型化。函数返回分配给它们的类型,不管参数是什么(可变或不可变)。

答案 2 :(得分:2)

问题是你在定义函数之前调用函数,而不包括声明。如果你添加这样一行:

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

在你的main()功能之前,你会没事的。默认(“implcit”)是假设函数返回int,并且具有空参数列表。依赖隐式声明是不好的形式,你应该明确声明所有函数。

答案 3 :(得分:1)

这与返回类型无关 - 这是你完全声明它的事实。发表声明:

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

在main()

中使用该函数之前

答案 4 :(得分:1)

正如每个答案(到目前为止)所说:为了避免错误,在使用之前声明函数,正如Chris Jester-Young指出的那样,使用标准机制来访问可变参数或遭受未定义行为的痛苦

在这个例子中,并不是因为函数的返回类型而需要在这种情况下使用之前的声明,而是因为它与未声明的函数的默认签名不匹配:int u();也就是说,一个函数采用未指定的参数返回int。具体来说,实际定义int maw(int,int,...)是与不一致的与假定的声明int maw()不一致,导致GCC说“'maw'的冲突类型”。

你的第二个例子

int main() {
  abc(); ..... return 0; 
}
abc() { ....... }

有效,因为abc的后续定义与遇到第一次调用时假定的默认签名不矛盾。但仅仅因为它的工作原理并不好,因为默认签名几乎没有类型安全性。

访问可变参数(与...匹配的参数)确实应该通过stdarg.h的标准机制来完成,除非您正在实现编译器并且是该编译器的作者{{1 }}。例如,在某些体系结构上,这些参数甚至可能不会在堆栈上传递,但它们仍然可以通过stdarg.h的宏来定位。

编辑:我重新编写了第二段,说明了我在不同的意思,我希望更清楚。

编译器需要在第一次调用之前知道函数是variadic ,因为在某些体系结构中,可能需要以与普通参数不同的方式传递可变参数。对于某些寄存器窗口RISK架构尤其如此,那些可能会在寄存器中传递前2个整数和前2个浮点数的架构,但即使寄存器中有空间,也必须将所有可变参数放在堆栈中。

在不使用stdarg.h调用约定的函数存在使用情况之前的类似声明。当将用Pascal或FORTRAN编写的模块与用C编写的模块链接时,通常会遇到这种情况。但是,核心DLL导出的许多Windows API函数都假定一个名为cdecl的调用约定,并且如果编译器要使用stdcall样式调用,程序将崩溃(整个机器在Windows 9x或更早版本上)。

答案 5 :(得分:0)

添加声明

int maw(int a,int b, ...);
main()函数之前

目前,您在main()函数中进行的调用隐式定义maw(),也就是说,编译器根据信息“猜测”maw()的返回类型是什么在main()内给出。如果在调用之前声明maw(),则不会出现此问题。

答案 6 :(得分:0)

调用实际接受可变数量参数的隐式声明函数具有未定义的行为。

我没有看到实际引用标准相关部分的答案。对于隐式函数定义,仅允许在C89和C90中使用。

从C89,§3.3.2.2,¶4:

  

如果括号中的括号参数列表之前的表达式   函数调用仅包含标识符,如果不包含   声明对于此标识符是可见的,标识符是   在包含的最里面的块中隐式声明   函数调用,声明          

extern int  identifier();
  出现了。

在同一节,¶6中,它声明调用一个后来被声明为采用可变数量的参数的隐式声明的函数会导致未定义的行为。

  

如果表示被调用函数的表达式具有类型   不包括原型,执行整体促销   每个参数和具有float类型的参数都被提升为   双。这些被称为默认参数促销。如果   参数的数量与参数的数量不一致,   行为未定义。如果使用类型定义函数   不包括原型,以及后面的参数类型   促销与之后的参数不兼容   促销,行为未定义。如果定义了函数   包含原型的类型,以及后面的参数类型   促销与参数的类型不兼容,或者如果   原型以省略号(“, ...”)结束,行为是   未定义。