前景提示:
c语言的标准从K&Rc,c89(ANSI c),c99,c11不断的发展,新标准不断的提出。但是,目前大部分编译器对c89完全支持,其他的标准可能就只是部分支持。c89是在K&Rc的基础发展而来,所以,K&R c 中的一些老式风格,许多编译还是支持。这就为我编写移植良好的程序带来许多问题。
函数定义:声明 + 具体实现 。它向编译器提供函数的信息,以及调用函数时的操作。
int fun( int a, int b)
{
a + b;
printf("%d",a);
return 0;
}
编译器得知的信息:
(1)函数的返回值
(2)参数的类型,参数的个数。
这都是声明做的工作,所以在后期调用函数时,编译器会检查函数的使用情况,确保函数正确调用。
函数实现就是 { .... } 花括号中的代码
老式的代码风格
int fun( a , b )
int a ;
int b;
{
a + b;
printf("%d",a);
return 0;
}
区别:参数的类型写在参数的列表和左花括号之间。
缺点:编译器只会记住函数 的返回类型,关于参数个数以及类型,编译器不管
#include<stdio.h>
int fun();//编译器记住的信息
int main( void )
{
int a = 1;
int b = 2;
fun( a );
}
int fun( a, b )
int a;
int b;
{
printf("%d",a);
return 0;
}
gcc 编译完全没问题,并且不会报错,或警告
函数声明:向编译器提供函数的信息,确保函数正确调用。
一般,向函数提供编译的信息有两种,一种是是没有参数的列表(老式风格),编译器只会记住返回值。另一种是函数原型(新式风格,现在我们一般写的,所提倡的)。
函数原型
int fun( int a, int b);
优点:编译器知道参数类型,个数,当参数不匹配,会向用户发错警告。
还有些关于原型你必须知道的事。
(1)函数原型像变量一样具有作用域。
int main( void )
{
{
int fun( int a ,int b);
fun();
}
......
{
int fun( int a ,int *b);
fun();
}
}
虽然你可能不这样写,但是编译器还是不会报错,你两个fun函数不一致,一定有哪里写错了,最好函数原型有文件作用域,放在头文件中,或者位于main函数前。
(2)函数原型中可以不写参数名
int fun( int , int );
编译器不关心变量名,实际上,代码翻译成机器码,根本没有变量名,有的只是一个个地址(不同地址对于不同变量)。但是程序是给人看的,所以在函数原型中的变量名最好能体现变量在函数中的作用,让程序员很快知道你函数的功能,这是一个不错的代码习惯。
没有参数列表(老式风格)
int fun( );
这种写法很常见,也许曾经就出现在你的代码中。你会说我这是一个参数为0的函数,可是编译器却不这么认为,即使你调用函数fun(a ,b ,c ..... ),这样写也不好出错。原因在于,只要声明函数时,不使用函数原型,编译器都不会记住参数相关信息。所以,区分有没有参数应该加void
int fun( void );
int fun( );//二义性,是新风格无参函数,还是旧风格 忽略参数列表的声明
#include<stdio.h>
//int fun(); 编译自动添加
int main( void )
{
float a = fun();
printf("%lf",a);
}
float fun()
{
return 3.14;
}
当你写的函数返回值为int,你忽略了编译可能不发出警告,因为它默认帮你添加。当函数返回值是其他类型,记得写函数原型很有必要。
函数原型:函数声明的一种情况。
关于他们的三角关系,有了一个大致的了解
定义实现了声明的功能,但不等同与声明。
原型是声明的一种 。
定义只能一次,声明可有多次(是不是想起,变量的定义和声明 )。
以上的所有情况,你或许见过,或许没见过,但是他们是真是存在的,好的代码风格,程序会有好的移植性