谈谈C语言中函数定义/声明冲突时的处理 |
您所在的位置:网站首页 › C语言c2198 › 谈谈C语言中函数定义/声明冲突时的处理 |
首先,本文中讨论的例子采用C语言,而非C++语言。 使用示例分析这个问题: 例子1 [cpp] view plain copy print ? #include void hello(int a); void hello(int a, int b); void hello(int a) { printf("hello, %d\n", a); } void main() { hello(1); } #include void hello(int a); void hello(int a, int b); void hello(int a) { printf("hello, %d\n", a); } void main() { hello(1); }
在这个例子中,hello()函数声明了两次,定义了一次。 VS2010的编译结果为: xx.c(4): warning C4031: second formal parameter list longer than the first list xx.c(7): warning C4029: declared formal parameter list different from definitionVC6的编译结果为: xx.c(4) : warning C4031: second formal parameter list longer than the first list xx.c(7) : warning C4029: declared formal parameter list different from definition xx.c(13) : error C2198: 'hello' : too few actual parameters
例子2 [cpp] view plain copy print ? #include void hello(int a); void hello(int a, int b); void hello(int a) { printf("hello, %d\n", a); } void hello(int a, int b); void main() { hello(1); } #include void hello(int a); void hello(int a, int b); void hello(int a) { printf("hello, %d\n", a); } void hello(int a, int b); void main() { hello(1); } 例子2比例子1多了一个函数声明(红色部分)。在这个例子中,hello()函数声明了三次,定义了一次。 VS2010的编译结果为: xx.c(4): warning C4031: second formal parameter list longer than the first list xxc(7): warning C4029: declared formal parameter list different from definition xx.c(11): warning C4031: second formal parameter list longer than the first list xx.c(15): error C2198: 'hello' : too few arguments for call VC6的编译结果为: xx.c.c(4) : warning C4031: second formal parameter list longer than the first list xx.c.c(7) : warning C4029: declared formal parameter list different from definition xx.c(15) : error C2198: 'hello' : too few actual parameters ---------------------------------------------------------------------------------------------------------------------------------------- 对比这两个例子,可以做出以下分析 1)在函数调用中,如果提供的参数数量小于函数原型(函数原型的确定,后面讨论)中要求的参数数量,则会报错“too few actual parameters”,在VS2010和VC6中均如此。 2)在存在多个函数声明、存在定义的情况下,如何确定函数原型? 有了(1)的分析结果,我们可以根据函数调用来确认函数原型,当不报错时,hello(int a)就是函数原型,报错时hello(int a, int b)才是函数原型。 具体分析过程不再讨论,说一下我粗略得到的结论: 同时存在多个函数声明和定义时, 在VS2010中,函数原型由所有声明(包含定义)中最后一条确定。 在VC6中,函数原型由所有声明(不包含定义)中的最后一条确定。 3)实际2)中的分析存在一个问题,“最后一条”,怎么算是最后一条,如果在函数调用后,还有函数声明呢?函数调用后的函数声明后还有函数调用呢?如何处理? 继续以实例分析. 例子3 [cpp] view plain copy print ? #include void hello(int a); void hello(int a, int b); void hello(int a) { printf("hello, %d\n", a); } void hello(int a, int b); void main() { hello(1); } void hello(int a); void callhello() { hello(1); } #include void hello(int a); void hello(int a, int b); void hello(int a) { printf("hello, %d\n", a); } void hello(int a, int b); void main() { hello(1); } void hello(int a); void callhello() { hello(1); } 例子3中,在main()函数后,又增加了一个函数声明和函数调用。VS2010编译结果如下(略去warning) xx.c(15): error C2198: 'hello' : too few arguments for call VC6编译结果如下(略去warning) xx.c(15) : error C2198: 'hello' : too few actual parameters VS2010和VC6处理一致,可以看到第一次hello()调用报错,第二次却没有报错。 这就说明,前后两次函数调用,用于检查函数调用是否正确的函数原型是不一样的: 我们很容易猜测到,用于检验函数调用的函数原型,是由源文件中、函数调用前、该函数的所有声明决定的。 可以继续验证: 例子4 [cpp] view plain copy print ? #include void hello(int a); void hello(int a, int b); void hello(int a) { printf("hello, %d\n", a); } void hello(int a, int b); void main() { hello(1); } void hello(int a, int b); void callhello() { hello(1); } #include void hello(int a); void hello(int a, int b); void hello(int a) { printf("hello, %d\n", a); } void hello(int a, int b); void main() { hello(1); } void hello(int a, int b); void callhello() { hello(1); } VS2010:xx.c(15): error C2198: 'hello' : too few arguments for call xx.c(22): error C2198: 'hello' : too few arguments for call VC6: xx.c(15) : error C2198: 'hello' : too few actual parameters xx.c(22) : error C2198: 'hello' : too few actual parameters 修改最后第2个hello()调用前的函数声明,第2个hello()调用也报错了。 结合上面的结论,可以得出: 同时存在多个函数声明和定义时, 用于检验函数调用的函数原型,是由源文件中、函数调用前、该函数的所有声明中的最后一条决定的; 在VS2010中,所有声明包含定义,在VC6中,所有声明不包含定义。 PS: 如果一个函数调用前,只存在这个函数的定义,那函数调用的检查肯定用函数定义中的函数原型,这一点要强调一下。
但是,同时,我们知道,函数原型不仅仅适用于函数调用的编译检查,还涉及到链接的问题。 不过在C语言中,对这方面,我们不需要太多考虑。因为链接依靠的是符号表,符号表中,函数的符号是由函数原型决定的。 但是C语言中没有name mangling机制,导致函数的符号实际只由函数名决定。 同样是一个例子: 例子5 [cpp] view plain copy print ? //main.c #include void hello(int a); void main() { hello(1); } //main.c #include void hello(int a); void main() { hello(1); } [cpp] view plain copy print ? //sub.c #include void hello(int a, int b) { printf("hello, %d %d\n", a, b); } //sub.c #include void hello(int a, int b) { printf("hello, %d %d\n", a, b); }1)后缀名为C,使用VS2010和VC6编译均正常,无报错。2)如果修改后缀名为C++,再编译, VS2010报错: main.obj : error LNK2019: unresolved external symbol "void __cdecl hello(int)" (?hello@@YAXH@Z) referenced in function _main xxxx.exe : fatal error LNK1120: 1 unresolved externals VC6报错: main.obj : error LNK2001: unresolved external symbol "void __cdecl hello(int)" (?hello@@YAXH@Z) xxxx.exe : fatal error LNK1120: 1 unresolved externals 从这一点来看,C语言对函数原型的检查机制,天然比不上C++的检查机制。
最后,再谈二个很有意思的东西。 1、旧式风格函数声明 例子6 [cpp] view plain copy print ? #include void hello(int a, int b) { printf("hello, %d, %d\n", a, b); } void hello(); void main() { hello(1, 2); } #include void hello(int a, int b) { printf("hello, %d, %d\n", a, b); } void hello(); void main() { hello(1, 2); } 这个程序会不会编译通过呢?根据我们上面得到的结论,VC6和VS2010,在检查hello(1,2)调用时,使用的是hello(),应该报错。 但是编译一下发现,程序正常通过编译,WHY? 这里涉及到一个旧式声明的问题。 void hello();既可以看成是一个旧式声明(只给出函数的返回类型),也可以看成没有参数的函数的新风格原型。 当然旧式声明早已经是垃圾堆里的东西了,但是编译器却要保证对旧式风格的兼容,因此hello()会被理解成一个旧式风格的声明。 SO.....void hello();不会影响到函数调用的检查。 我已经尝试过,在上面所有的例子中,随意添加void hello();不会影响编译结果的。
2、过多的函数参数 例子7 [cpp] view plain copy print ? #include void hello(int a) { printf("hello, %d\n", a); } void main() { hello(1, 2); } #include void hello(int a) { printf("hello, %d\n", a); } void main() { hello(1, 2); } 上面什么情况??过多的函数参数,看一下编译结果把:VS2010:(warning level3) XX.C(10): warning C4020: 'hello' : too many actual parameters VC6:(warning level3) XX.C(10) : warning C4020: 'hello' : too many actual parameters至少在:(warning level3)下,是仅有WARNING,没有报错的。 运行一下,也是正常的。
那修改后缀名为c++?? VS2010: 1>ClCompile: 1> main.cpp 1>f:\prj_cs\c\try_0819\try_0819\main.cpp(10): error C2660: 'hello' : function does not take 2 arguments VC6: d:\my documents\test\test0818\test.c(10) : warning C4020: 'hello' : too many actual parameters Linking... test.obj : error LNK2005: _main already defined in main.obj main.obj : error LNK2001: unresolved external symbol "void __cdecl hello(int)" (?hello@@YAXH@Z) Debug/test0818.exe : fatal error LNK1120: 1 unresolved externals 不太一样,一个编译就报错,一个链接才报错,但终究也是都报错了。 由此又印证了,C的一些天然能力不足~~~。
至于具体标准里如何规定的,后续有时间打算再研究一下标准。。。。 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |