c语言声明函数位置 c语言声明函数和定义函数( 二 )


C语言中函数声明的位置有几种?对函数的“定义”和“声明”不是一回事 。函数的定义是指对函数功能的确立 , 包括指定函数名 , 函数值类型、形参及其类型以及函数体等,它是一个完整的、独立的函数单位 。而函数的声明的作用则是把函数的名字,函数类型以及形参的类型、个数和顺序通知编译系统,以便在调用该函数时进行对照检查(例如,函数名是否正确,实参与形参的类型和个数是否一致),它不包括函数体 。——谭浩强  , 《C程序设计》(第四版),清华大学出版社 , 2010年6月,p182
这段论述包含了许多概念性错误,这些概念错误在许多C语言书中都同样普遍存在 。为了说明这些错误,首先来回顾一下C语言演变和发展的一些情况 。
最早,C语言的代码可以这样写:
main(){ printf("hello,world!\n");}
注意 , 这段代码对标识符printf没有进行任何说明 。这是因为printf()函数的返回值为int类型 。当时的C语言规定,对于没有任何说明的函数名 , 编译器会默认为返回值为int类型,因此对这样的函数名可以不做任何说明 。那个时期的C语言,很多情况下int可以不写 。例如main()函数返回值的类型为int就可以不写 。
但是需要特别说明的是,这种“省劲”的写法已经过时,从C90标准起 , 这种写法就步入了被逐步抛弃的过程(尽管当时还没有完全立即废止) 。C99废除了隐式函数声明法则(remove implicit function declaration),另外,省略main()前面的int也已经不再容许了 。
在C语言早期,尽管有时不需要对函数名进行说明,但有些情况下对函数名进行说明还是必须的,比如:
?
12345
double sqrt();int main(){ printf("%f\n" , sqrt(9.) );}
这是因为函数sqrt()返回值的类型不是int类型而是double类型,编译器编译时需要知道sqrt(9.)这个表达式的类型 。
不难注意到这种对函数名的说明非常简单 , 这是最早期的一种函数类型说明的形式 。这种说明只着重说明函数名是一个函数及其返回值类型,如果程序员在调用函数时存在参数类型或个数方面的错误编译器是无法察觉的,因为函数类型说明中“()”内没有任何信息 。
这种办法只说明了函数名与()进行运算的结果也就是函数返回值的数据类型,无法进一步检查参数方面的错误是这种写法的不足之处 。
如果不写函数类型说明,也可以把函数定义写在函数调用之前:
?
123456789
double square ( double x){ return x * x ;}int main(void){ printf("%f\n" , square(3.) ); return 0;}
这表明函数定义也具有对函数名的类型加以说明的效果,因此从这个意义上来说,函数定义也是一种对函数类型的说明 。这种办法可以检查出函数调用时在参数个数和类型方面的错误 。
但是,用这种办法说明函数名并不好,因为这样做在编程时还需要考虑应该把哪个函数定义写在前面,哪个写在后面的问题 。假如函数A调用函数B,函数B调用函数C,函数C又调用函数A,究竟如何安排函数定义的顺序就会让人感到无所适从 。此外这种办法也不利于代码的组织,在由多个源文件组成的源程序时,这种写法就更会捉襟见肘、漏洞百出 。因此,在1990年 , C标准借鉴C++语言规定了一种新的说明函数名的方法 , 这就是函数原型(Function Propotype)式说明函数类型的方法:
?
12345678910
double square ( double ); //或 double square ( double x)int main(void){printf("%f\n" , square(3.) );return 0;}double square ( double x){return x * x ;}
使用这种办法,不但可以检查函数调用时参数类型和个数方面的错误,同时解决了源代码的组织问题 , 因为程序员不必再考虑该把哪个函数写在前面、哪个写在后面这种无聊的问题了 。这种办法全面地说明了函数名的数据类型 。此外要说明的是,把形参及其数据类型写在“()”内形式的函数定义也属于函数原型(Function Propotype)的范畴 。

推荐阅读