函数
一个函数的定义包括:返回类型、函数名、多个形参、函数体。
函数调用:
1.用实参初始化函数定义的形参
2.将控制权转移给被调函数
return语句:
1.返回return语句中的值(如果有)
2.将控制权从被调函数转移回主调函数
实参的类型必须和相对应的形参类型匹配,顺序尽量一致。函数调用规定实参数量应该与形参数量一致,所以形参一定会被初始化。
形参列表中的形参通常用逗号隔开,每个形参都是含有一个声明符的声明。
任意两个形参名字不能同名,函数最外层作用域中的局部变量也不能使用与函数形参一样的名字。
即使形参不被函数使用,也必须为它提供一个实参。
局部对象
局部变量仅在函数的作用域中可见,同时局部变量还会隐藏在外层作用域中同名的其他所有声明中。
把只存在于块执行期间的对象称为自动对象,当块的执行结束后,块中创建的自动对象的值就变成未定义。
形参是一种自动对象
我们用传递给函数的实参初始化形参所对应的自动对象。
对于局部变量对应的自动对象:1. 如果变量定义本身含有初始值,就用这个初始值初始化,2.如果变量定义本身不含有初始值,执行默认初始化(内置类型的未初始化局部变量将产生未定义的值)
如何使局部变量的生命周期变长呢?
使用局部静态变量。局部静态对象在程序的执行路径第一次经过对象定义语句时初始化,直到程序终止才销毁(和普通局部对象生命周期不同),即使对象所在的函数结果结束执行也没有影响。
如果局部静态变量没有显式初始化,将执行值初始化,内置类型的局部静态变量初始化为0
函数声明
函数只能定义,但可以声明多次。如果一个函数永远不会用到,可以只有声明没有定义。
函数声明和函数定义的唯一区别就是函数声明无须函数体,用一个分号代替。
可以省略形参的名字,但一般不省略(更好理解函数功能)
之前说过,变量在头文件中声明,在源文件中定义,函数也是如此,在头文件中声明在源文件中定义。
定义函数的源文件应该把含有函数声明的头文件包含进来,编译器负责验证函数的定义和声明是否匹配。
参数传递:
每次调用函数时都会重新创建它的形参,并用传入的实参对形参进行初始化。
如果形参是引用类型,则它将绑定到对应的实参上,否则将实参的值拷贝到形参。
1.当形参是引用类型时,可以说对应的实参被引用传递或者函数被传引用调用,引用形参是它绑定的实参的别名(即引用形参是对应实参的别名)
2.当实参的值被拷贝到形参时,形参和实参是两个独立的对象,可以说实参被值传递或者函数被传值调用
使用引用避免拷贝
拷贝大的类类型对象或者容器对象比较低效,甚至有些类类型不支持拷贝操作。所以我们可以使用引用避免拷贝,函数只能通过引用形参访问该类型。
比较两个string对象的长度:
bool isShorter(const string &s1,const string &s2)
return s1.size() < s2.size();
如果函数无须改变引用形参的值,最好将其声明为常量引用。
const 形参和实参
顶层const作用于对象本身。当用实参初始化形参时会忽略掉顶层const。即当形参中有顶层const,传给他常量对象或者非常量对象都可以。
有一个忽略顶层const的误区:
void fcn(const int i){
}
void fcn(int i){
}
上面两个函数第二个有错误,看似不同,但因为顶层const被忽略了,所以传入两个函数的参数可以完全相同。(重复定义了fcn(int i))
根据之前的初始化规则,可以使用非常量初始化一个底层const对象,同时一个普通的引用必须用同类型的对象初始化。
不能把const对象、字面值或者需要类型转换的对象传递给普通的引用形参。
数组形参
数组有两个特殊性质:
1.不允许拷贝数组 -----》无法使用值传递使用数组参数
2.使用数组名转换为指针,当我们为函数传递一个数组时,实际上传递的是指向数组首元素的一个指针。
形式不同但是函数等价:每个函数的唯一形参就是const int*类型的
void print(const int*);
void print(const int[]);
void print(const int[10]);
数组是以指针的形式传递给函数的,应该提供一些额外的信息
1.使用标记指定数组长度,数组本身包含一个结束标志
C风格字符串存储在字符数组中,而且在最后一个字符后跟着一个空字符,函数在处理C风格字符串时遇到空字符停止。
void print(const char *cp){
if(cp)
while(*cp)
cout << *cp++;
}
2.使用标准库规范,成员函数begin()和end()
void print(const int *beg, const int *end){
while(beg != end)
cout << *beg++ << endl;
}
3.显式传递一个表示数组大小的形参
void print(const int ai[], size_t size){
for(size_t i = 0; i!=size; ++i)
cout << ai[i] << endl;
}
int j[] = {
0,1};
print(j, end(j) - begin(j));
当函数不需要对数组元素执行写操作时,数组的形参应该是指向const的指针。
只有当函数确定要改变元素值时,才把形参定义为指向非常量的指针。
数组引用形参
void print(int (&arr)[10]){
for(auto i : arr)
cout << i <<endl;
}
f(int &arr[10])
将arr声明为引用的数组,数组内是10个引用
f(int (&arr)[10])
arr是具有10个整数的整型数组的引用
限制了print函数的可用性,只能作用于大小为10的数组。
传递多维数组
真正传递的是指向数组首元素的指针,而首元素本身就是一个数组ÿ