一劳永逸地理解C和C++中f()和f(void)之间的区别

42 c c++ function void

好的,所以我听到了关于这个主题的不同意见,只是想确保我理解正确.

对于C++

声明void f();void f(void);意思完全相同,功能f不带任何参数.同样的定义.

对于C.

声明void f(void);意味着f不带任何参数.

声明void f();意味着函数f可能有也可能没有参数,如果有,我们不知道它们是什么类型的参数,或者它们有多少参数.请注意,它与省略号不同,我们无法使用va_list.

现在这里的事情变得有趣了.

情况1

宣言:

void f();
Run Code Online (Sandbox Code Playgroud)

定义:

void f(int a, int b, float c)
{
   //...
}
Run Code Online (Sandbox Code Playgroud)

案例2

宣言:

void f();
Run Code Online (Sandbox Code Playgroud)

定义:

void f()
{
   //...
}
Run Code Online (Sandbox Code Playgroud)

题:

当我们f使用正确的参数,错误的参数和根本没有参数调用时,在情况1和2的编译时会发生什么?运行时会发生什么?

附加问题:

如果我f用参数声明,但没有它们来定义它,它会有所作为吗?我应该能够解决函数体中的参数吗?

Die*_*Epp 53

更多术语(C,而不是C++):函数的原型声明其参数的类型.否则该函数没有原型.

void f();                      // Declaration, but not a prototype
void f(void);                  // Declaration and prototype
void f(int a, int b, float c); // Declaration and prototype
Run Code Online (Sandbox Code Playgroud)

从K&R C时代开始,非原型的声明就是来自ANSI C之前的保留.使用旧式声明的唯一原因是保持与旧代码的二进制兼容性.例如,在Gtk 2中有一个没有原型的函数声明 - 它是偶然的,但是如果不破坏二进制文件就不能删除它.C99标准评论:

6.11.6函数声明符

使用带有空括号的函数声明符(不是prototype-format参数类型声明符)是一个过时的功能.

建议:我建议编译所有的C代码在GCC /铛与-Wstrict-prototypes-Wmissing-prototypes,除了通常的-Wall -Wextra.

怎么了

void f(); // declaration
void f(int a, int b, float c) { } // ERROR
Run Code Online (Sandbox Code Playgroud)

声明不同意功能机构!这实际上是一个编译时错误,这是因为你float没有原型的函数中没有参数.你不能float在非原型函数中使用a的原因是因为当你调用这样一个函数时,所有参数都会使用某些默认促销来提升.这是一个固定的例子:

void f();

void g()
{
    char a;
    int b;
    float c;
    f(a, b, c);
}
Run Code Online (Sandbox Code Playgroud)

在这个程序中,a被提升为int1c被提升为double.所以定义f()必须是:

void f(int a, int b, double c)
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

见C99 6.7.6第15段,

如果一个类型具有参数类型列表而另一个类型由函数声明符指定,该函数声明符不是函数定义的一部分并且包含空标识符列表,则参数列表不应具有省略号终止符,并且每个参数的类型应为与应用默认参数促销产生的类型兼容.

答案1

当我们f使用正确的参数,错误的参数和根本没有参数调用时,在情况1和2的编译时会发生什么?运行时会发生什么?

当您致电时f(),参数将使用默认促销进行宣传.如果提升的类型与实际参数类型匹配f(),那么一切都很好.如果它们不匹配,它可能会编译,但你肯定会得到未定义的行为.

"未定义的行为"是指"我们不保证会发生什么".也许你的程序会崩溃,也许它会正常工作,也许会邀请你的公婆吃饭.

有两种方法可以在编译时获得诊断.如果您有一个具有跨模块静态分析功能的复杂编译器,那么您可能会收到一条错误消息.您还可以使用GCC获取非原型函数声明的消息-Wstrict-prototypes- 我建议您在所有项目中打开(使用Gtk 2的文件除外).

答案2

如果我f用参数声明,但没有它们来定义它,它会有所作为吗?我应该能够解决函数体中的参数吗?

它不应该编译.

例外

实际上有两种情况允许函数参数不同意函数定义.

  1. 可以传递char *给期望的函数,void *反之亦然.

  2. 可以将有符号整数类型传递给期望该类型的无符号版本的函数,反之亦然,只要该值在两种类型中都是可表示的(即,它不是负数,并且不在范围之外)签名类型).

脚注

1:这是有可能的是char提升到unsigned int,但是这是非常罕见的.

  • 您应该更仔细地阅读错误。它与空列表本身无关。这与您选择的参数(浮点数)有关。它不允许带有默认提升(float->double)的参数。您会发现,如果将该浮点数更改为 int 或 double,您的错误就会消失。 (2认同)

pax*_*blo 6

如果您使用的是C99或更高版本(并且除非您坚持使用旧的嵌入式系统或类似的东西,您可能应该使用更现代的东西),整个事情真的是一个没有实际意义的事情.

C99/C11部分6.11.6 Future language directions, Function declarators规定:

使用带有空括号的函数声明符(不是prototype-format参数类型声明符)是一个过时的功能.

因此,你应该避免使用像这样的东西void f();.

如果它需要参数,列出它们,形成一个合适的原型.如果没有,我们void明确表示它不采取任何参数.