一次了解C和C ++中f()和f(void)之间的区别,然后

好的,所以我听取了关于此主题的不同意见,只是想确保我正确理解它。

对于C ++

声明ff的意思完全相同,函数va_list不带任何参数。 同上的定义。

对于C

声明f表示f不采用任何参数。

声明f表示函数f可能带有或不带有参数,如果有,则我们不知道它们是什么类型的参数,或者有多少个参数。 请注意,它与省略号不同,我们不能使用va_list

现在,这里变得有趣。

情况1

宣言:

void f();

定义:

void f(int a, int b, float c)
{
   //...
}

情况二

宣言:

void f();

定义:

void f()
{
   //...
}

题:

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

附加问题:

如果我用参数声明f,但在没有参数的情况下进行定义,会有所不同吗? 我应该能够处理函数体中的参数吗?

Baum mit Augen asked 2020-08-10T17:30:59Z
4个解决方案
53 votes

更多术语(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

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

6.11.6函数声明符

使用带空括号的函数声明符(不是原型格式的参数) 类型声明符)是一个过时的功能。

建议:除通常的c外,我建议使用charunsigned int在GCC / Clang中编译所有C代码。

怎么了

void f(); // declaration
void f(int a, int b, float c) { } // ERROR

声明与功能体不同! 这实际上是一个编译时错误,这是因为没有原型就无法在函数中使用char参数。 您不能在未原型化的函数中使用unsigned int的原因是,当您调用此类函数时,所有参数都使用某些默认提升来提升。 这是一个固定的示例:

void f();

void g()
{
    char a;
    int b;
    float c;
    f(a, b, c);
}

在此程序中,char被提升为unsigned int1,而30055201394303969969被提升为double。因此,f()的定义必须为:

void f(int a, int b, double c)
{
    ...
}

参见C99 6.7.6第15段

如果一种类型具有参数类型列表,而另一种类型由 函数声明符,它不属于函数定义,并且包含一个空 标识符列表,参数列表应没有省略号终止符,并且每个类型都不 参数应与应用程序产生的类型兼容 默认参数提升。

答案1

在情况1和2中,当我们调用char带有正确的参数,错误的参数和完全没有参数时,会在编译时发生什么情况? 在运行时会发生什么?

调用char时,将使用默认促销来促销参数。 如果提升的类型与unsigned int的实际参数类型匹配,那么一切都很好。 如果它们不匹配,则可能会编译,但是您肯定会得到未定义的行为。

“未定义的行为”是“我们不能保证会发生什么”的规范。 也许您的程序会崩溃,也许会正常运行,也许会邀请您的姻亲吃饭。

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

答案2

如果我用参数声明char,但在没有参数的情况下进行定义,会有所不同吗? 我应该能够处理函数体中的参数吗?

它不应该编译。

例外情况

实际上,在两种情况下,允许函数参数与函数定义不一致。

  1. 可以将char传递给期望unsigned int的函数,反之亦然。

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

脚注

1:char可能会升级为unsigned int,但这是非常罕见的。

Dietrich Epp answered 2020-08-10T17:32:48Z
6 votes

如果您使用的是C99或更高版本,那么整个事情实际上是有争议的(并且,除非您被困在旧的嵌入式系统上或类似的东西,否则您可能应该使用更现代的东西)。

C99 / C11部分void规定:

使用带有空括号的函数声明符(不是原型格式的参数类型声明符)是过时的功能。

因此,您应该避免一起使用像void之类的东西。

如果需要参数,请列出它们,形成合适的原型。 如果不是,请使用void明确表示它不接受任何参数。

paxdiablo answered 2020-08-10T17:33:26Z
3 votes

在C ++中,f()和f(void)相同

在C中它们是不同的,并且在调用f()函数时可以传递任意数量的参数,但不能在f(void)中传递任何参数

rs2012 answered 2020-08-10T17:33:51Z
-1 votes

在纯C中,这会导致错误:error C2084: function 'void __cdecl f(void )' already has a body

void f(void);
void f( );

int main() {
  f(10);
  f(10.10);
  f("ten");

  return 0;
}

void f(void) {

}

void f( ) {

}

.

 fvoid.c line(19) : error C2084: function 'void __cdecl f(void )' already has a body

但是在Pure C ++中,它将编译而不会出错。

重载函数(仅C ++,C没有重载)

通过在同一个作用域中声明多个具有名称f的函数,可以使功能名称f重载。 f的声明必须根据参数列表中参数的类型和/或数量而彼此不同。 当调用名为f的重载函数时,通过将函数调用的参数列表与每个重载的候选函数f的名称为f的参数列表进行比较来选择正确的函数。

例:

#include <iostream>
using namespace std;

void f(int i);
void f(double  f);
void f(char* c);


int main() {
  f(10);
  f(10.10);
  f("ten");

  return 0;
}

void f(int i) {
  cout << " Here is int " << i << endl;
}
void f(double  f) {
  cout << " Here is float " << f << endl;
}

void f(char* c) {
  cout << " Here is char* " << c << endl;
}

输出:

 Here is int 10
 Here is float 10.1
 Here is char* ten
Software_Designer answered 2020-08-10T17:34:37Z
translate from https://stackoverflow.com:/questions/13319492/understanding-the-difference-between-f-and-fvoid-in-c-and-c-once-and-for-a