更多术语(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之前遗留下来的.使用旧式声明的唯一原因是为了保持与旧代码的二进制兼容性.例如,在GTK2中,有一个没有原型的函数声明--它意外地出现在那里,但是如果不 destruct 二进制文件就无法删除它.C99标准注释:
6.11.6函数声明符
使用带有空括号的函数声明符(不是原型格式参数
我建议用GCC/Clang编译所有的C代码,除了通常的-Wall -Wextra
外,还要用-Wstrict-prototypes
和-Wmissing-prototypes
.
会发生什么
void f(); // declaration
void f(int a, int b, float c) { } // ERROR
声明与函数体不一致!这实际上是一个compile time错误,这是因为在没有原型的情况下,函数中不能有float
个参数.不能在非原型化函数中使用float
的原因是,当您调用这样的函数时,所有参数都会使用某些默认提升来提升.下面是一个固定的示例:
void f();
void g()
{
char a;
int b;
float c;
f(a, b, c);
}
在这个程序中,a
提升到int
1,c
提升到double
.所以f()
的定义必须是:
void f(int a, int b, double c)
{
...
}
参见C99 6.7.6第15段,
如果一种类型具有参数类型列表,而另一种类型由
答案1
在 case 1和 case 2中,当我们使用正确的参数、错误的参数和完全没有参数调用f
时,在编译时会发生什么?运行时会发生什么?
当您调用f()
时,参数将使用默认的升级方式升级.如果升级的类型与f()
的实际参数类型匹配,则一切正常.如果它们不匹配,它将被编译,但你肯定会得到未定义的行为.
"未定义的行为"指的是"我们不保证会发生什么."也许你的程序会崩溃,也许它会工作得很好,也许它会邀请你的姻亲共进晚餐.
有两种方法可以在编译时获取诊断信息.如果您有一个具有跨模块静态分析功能的复杂编译器,那么您可能会收到一条错误消息.您还可以使用GCC获取非原型函数声明的消息,使用-Wstrict-prototypes
——我建议在所有项目中启用-Wstrict-prototypes
(使用GTK 2的文件除外).
回答2
如果我用参数声明f
,但不带参数定义它,会有什么不同吗?我应该能够处理函数体中的参数吗?
它不应该编译.
例外情况
实际上有两种情况允许函数参数与函数定义不一致.
可以将char *
传递给预期为void *
的函数,反之亦然.
可以将带符号整数类型传递给需要该类型的无符号版本的函数,反之亦然,只要值在这两种类型中都可表示(即,它不是负数,并且没有超出带符号类型的范围).
脚注
1:char
提升到unsigned int
是possible,但这很少见.