C++创建使用一个函数必须完成三个工作:
提供函数原型
提供函数定义
调用函数
首先讲函数定义的通用格式:
typeName functionName(parameterList)
{
statements
return value
}
如果这个函数没有返回值的话,typeName就是void,return value这一行也可以省略。C++的函数可以返回任何类型:整数,浮点数,指针,结构或者对象,但就是不能返回数组。
paramentList指定了传递给函数的参数类型和数量,具体稍后会介绍。
函数原型通常写在头文件中,原型是什么呢?原型就是函数到编译器的接口:它将函数返回值的类型(如果有的话)以及参数的类型和数量告诉编译器。
函数原型是一条语句,必须以分号结束。获取原型最简单的方法是复制函数定义的第一行,并添加分号,比如我们有一个行数
double cube (double x)
{ return x*x*x;}
函数原型可以写为
double cube(double x);
也可以省略变量名,直接写成
double cube(double);
原型的功能就是确保以下几点:
编译器正确处理函数返回值;
编译器检查使用的参数数目是否正确;
编译器检查使用的参数类型是否正确,如果不正确,则转为正确的类型(如int转成double)
下面介绍函数参数,还是看上面的cube函数,main中我们这样调用:
double side = 12.3;
double volume = cube(side);
这样调用cube函数时,该函数会创建一个新的名为x的变量,并将side的值(12.3) 赋给x,然后对x进行计算。这样就不会对side造成任何影响。
用于接收传递值得变量(x)被称为形参,传递给函数的值被称为实参。
那么如果main中也有个x变量怎么办呢?答案是main里面的x和cube里面的x没有任何关系,cube里面的x被称为局部变量,只为cube函数存在,所以cube函数一调用完,cube里面的x就会被释放。这样的变量也叫做自动变量,因为他们是在程序执行过程中自动被分配和释放的。
函数可以有多个不同类型的参数,具体操作和单个参数一样,函数定义和原型声明的时候用逗号隔开。
前面说了函数的参数和返回值不能是数组,但现实中我们需要很多对数组的操作,怎么办呢?先讲怎么把数组作为参数传递给函数:
其实看完这章之后我们可以完全把数组放在函数的参数里面:
int sum_arr(int arr[], int n);
这完全合法!但这个时候c++不当arr是数组,而把它看成一个指针!但在编写函数的其余部分是,程序员可以将arr看成数组。(有点晕啊~~)。程序清单7.5很好的证明了这个用法。
其实c++把数组名视为指针,比如我们有一个数组
int cookies[10];
这个数组的数组名cookies其实就是这个数组第一个元素的地址:
cookies = &cookies[0]
所以在sum_arr函数中我们传递的是这个数组第一个元素的地址(这个是可以有的)。因为cookies是int的数组,所以数组名cookies的类型实际上是一个int指针int *,而这个函数的正确写法应该是
int sum_arr(int *arr, int n).
注意的是:在C++中,当且仅当用于函数头或者函数原型中int *arr和int arr[] 的含义才相同。
读到这里我们明白函数并没有将数组内容传递给函数,而只是将数组的位置,包含的元素种类传递给函数。这样好处就是避免传递数据量很大的数组,但风险就是现在可以对数组中的任何一个元素进行操作。如果要避免这种操作,就必须在函数头和原型中在数组参数前面加const限定符:
int sum_arr(const int arr[], int n]);
还需要注意的是,这里要使用arr[]数组作为参数,我们一般还得把数组的长度传给函数(上例中的int n),另外一个做法就是使用数组区间,把需要使用的数组区间的第一个元素和最后一个元素传给函数(这里他们都被看成指针)
const用于指针也比较微妙,可以用两种不同的方式将const关键字用于指针:
让指针指向一个常量对象,这样可以防止使用该指针来修改所指向的值;
指针本身声明为常量,这样可以防止改变指针指向的位置
比如:
int a = 10;
const int *pa = &a;
*pa = 20; //可以,因为pa指向的a不是const
*pa +=1; //不可以,pa指向的地址不能改变
另外可以将const变量的地址赋给指向const的指针,但不能将const的地址赋给常规指针
比如
const int a = 10;
int *pa = &a; //这样就是非法的了,pa* 也必须是const: const int *pa = &a;
到这里就有点晕了,更晕的是const和可以这样放
int * const pa = &a. 什么区别呢,把书上的例子搬过来先吧:
int a = 1;
int b = 2;
const int *p1 = &a;
*p1 = 20; //不可以,因为*p禁止修改它所指向的值
p1 = &b;// 可以,*p可以指向另一个地址
int *const p2 = &a;
*p2 = 20; // 可以
p2 = &b; // 不可以
但愿读者这个时候能看出区别了把:
const放最前面,表示(int *p1) 不能改,即p1指向的地址的值
const放中间表示(p2)不能修改,即p2指向的地址
如果数组是二维的,那么放进去函数参数列表的数组名表示二维数组的第一行第一个元素的地址 - 这个指针指向包含n(n等于二维数组的列数)元素的数组。
另外C风格字符串本身也是一个数组,将字符串作为参数时就意味着传递的是第一个字符的地址。