好的,所以我听到了关于这个主题的不同意见,只是想确保我理解正确。
对于 C++
声明 void f();
和 void f(void);
意思完全一样,函数 f
不带任何参数。定义同上。
对于 C
声明 void f(void);
表示 f
不带任何参数。
声明 void f();
表示函数 f
可能有也可能没有参数,如果有,我们不知道这些参数是什么类型的,或者有多少个。请注意,它与省略号不同,我们不能使用 va_list
。
现在事情变得有趣了。
情况1
宣言:
void f();
定义:
void f(int a, int b, float c)
{
//...
}
案例2
宣言:
void f();
定义:
void f()
{
//...
}
问题:
在案例 1 和 2 中,当我们调用 f
时使用正确的参数、错误的参数和根本没有参数时会发生什么?运行时会发生什么?
附加问题:
如果我声明 f
带有参数,但在没有参数的情况下定义它,会有所不同吗?我应该能够解决函数体中的参数吗?
原文由 user500944 发布,翻译遵循 CC BY-SA 4.0 许可协议
更多术语(C,不是 C++):函数的原型声明其参数的类型。否则该函数没有原型。
不是原型的声明是 ANSI C 之前的保留,从 K&R C 时代开始。使用旧式声明的唯一原因是保持与旧代码的二进制兼容性。例如,在 GTK 2 中有一个没有原型的函数声明——它是偶然出现的,但是如果不破坏二进制文件就不能删除它。 C99 标准评论:
建议: 除了通常的
-Wall -Wextra
之外,我建议使用-Wstrict-prototypes
和-Wmissing-prototypes
编译 GCC/Clang 中的所有 C 代码。怎么了
声明与函数体不一致!这实际上是一个 编译时 错误,这是因为在没有原型的函数中不能有
float
参数。您不能在非原型函数中使用float
的原因是,当您调用此类函数时,所有参数都会使用某些默认提升来提升。这是一个固定的例子:在这个程序中,
a
被提升为int
1和c
被提升为double
所以f()
的定义必须是:见 C99 6.7.6 第 15 段,
答案 1
当您调用
f()
时,参数会使用默认促销进行促销。如果提升的类型与f()
的实际参数类型匹配,那么一切都很好。如果它们不匹配,它 可能会 编译,但你肯定会得到未定义的行为。“未定义的行为”是规范的说法,即“我们不保证会发生什么”。也许你的程序会崩溃,也许它会正常工作,也许它会邀请你的姻亲来吃饭。
有两种方法可以在编译时获得诊断。如果您有一个具有跨模块静态分析功能的复杂编译器,那么您可能会收到一条错误消息。您还可以使用
-Wstrict-prototypes
使用 GCC 获取非原型函数声明的消息——我建议在您的所有项目中打开它(使用 GTK 2 的文件除外)。答案 2
它不应该编译。
例外
实际上有两种情况允许函数参数与函数定义不一致。
可以将
char *
传递给需要void *
--- 的函数,反之亦然。可以将有符号整数类型传递给期望该类型的无符号版本的函数,反之亦然,只要该值在两种类型中都是可表示的(即,它不是负数,并且不超出签名类型)。
脚注
1 : 有 可能
char
为unsigned int
,但这种情况非常少见。