一、函数原型
函数原型作用:描述函数的输入参数及类型、输出类型以及名称,并将其告诉编译器。
为什么编译器需要函数原型呢
第一:效率不高。如果没有函数原型只有函数定义,编译器在编译main函数碰到调用函数的位置时,需要搜索文件找到文件定义的地方,main的编译也会停止。
第二:函数有可能不在main()所在文件中。C++允许将一个程序放在多个文件中,单独编译这些文件,再将它们组合起来。这种情况下,如果该函数未在main()所在的文件中定义时,例如函数代码代码在库中、函数代码在其他文件中,可能无权限访问函数代码。
二、数组作为函数参数时
(1)C++会视数组名为指针:
char name[20]="i love c++";
当使用数组作为参数时:int checksum(char name[],int charLength); -------------函数原型1
C++会视数组名name[]为指针,将其当成指针进行处理:
int checksum(char *name,int charLength); ---------------------------------------------- 函数原型2
函数原型1和函数原型2,完全等价。
因此,不同与其他常规变量(可以选择值传递或者指针传递),当使用数组作为函数参数时,使用的是指针传递,函数中对形参的修改会影响到实参。
(2) 为什么需要charLength呢:
由于数组作为参数时,C++会视其为指针,使用sizeof(name)不会得到数组的长度,而会得到指针在内存中占的字节(4或者8),因此必须要额外传入数组的长度。
三、const的使用
1、const作用:
限制程序对变量的修改。
2、const与指针:
(1)const在*之后,说明指针指向的对象不能修改,指针可以修改。
(2)const在*之后,说明指针指向的对象能修改,指针不可以修改。
3、const的使用:
场景1:
如果传递一个数组到函数中,如二所述,只能使用指针传递,使用指针传递时,对形参的修改会影响到实参, 但是,我并不想修改数组的值,只想取数组中的数据进行操作。
这种情况下,需要const对形参进行限制:
int checksum( const char *name,int charLength);
const char *name,const在*之前,说明name指向的实参不允许修改。
场景2:
需要传递结构体作为参数,但是这个结构体特别特别大,如果使用值传递,会在内存中对该结构体进行拷贝,严重的影响运行速度。
这种情况下,可以选择指针传递加const组合。
int checksum( const typename* name);
const typename* name,const在*之前,说明name指向的typename类型的结构体实参不允许修改。
4、尽量使用const
(1)当不想对数据进行修改时,添加const修饰可以避免无意间修改指针导致错误
(2)当const修饰函数参数时,可以接受const与非const的变量;否则,只能接收非const变量。
四、二维数组作为函数参数
两种表示:
char name[3][4];
(1) int checksum(int name[][4],int size);
(2) int checksum(int (*name)[4] ,int size);
(1)与(2)完全等价,int name[][4]被C++视为int (*name)[4],即指向一个由4个int组成的数组,因此指针指定了列数,行数需要传入即size。
在函数中对二维数组进行操作时,可以使用name[i][j]的方式进行取值和赋值操作。虽然有其他的取值方法例如: *(*(name+2)+2),但是没必要,很难看。
注:const只适用于基本的指针,而name是指向指针的指针,因此不可以使用const进行修饰。
五、函数指针
函数指针:存放函数地址的指针。
(1)函数指针声明:
函数原型为:int checksum(int),其对应的函数指针为:int (*pf)(int);相当于将checksum使用(*pf)替代了。
(2)函数指针的使用:
int checksum(int);
int checksum1(int);
int (*pf)(int);
…………
…………
pf = checksum;
int res = pf(3); -------- 等价于int res = checksum(3);
pf = checksum1;
int res1 = pf(4);---------等价于int res1 = checksum1(4);
函数名称就相当于函数地址。 使用函数指针时,将函数名称赋值给函数指针,调用时直接将函数指针视为函数名称即可。