基于C++primer plus(6th)
函数
使用函数有三步:
- 函数定义
- 函数原型
- 调用函数
函数定义
典型的函数定义如下:
type functionName (parameterlist){
body;
return data;
}
除了void
函数没有返回值,其他函数都有返回语句。
- 返回值类型应该与定义一致,或可强制转换类型。
- 返回值可以是整数、浮点、指针及结构等,但是不能为数组。
函数原型
原型描述了函数到编译器的接口,所以在main()
函数之前需要进行原型的声明。
函数原型最简单的声明方法就是复制函数定义的函数头,但是函数原型只需要提供参数类型,不需要提供参数名(当然带着无妨,Ctrl+V更方便)。
函数参数
参数调用与定义均用,
来分隔,即使参数类型一致也需要分别定义声明,函数定义的参数列表里即形参,函数调用时传入的变量等数据对象即实参。
函数中声明的变量是局部变量,函数调用时生成在函数里,调用结束即销毁。
函数与数组
大多数情况下C++将数组名视为指针,即将数组名解释为第一个元素的地址:array = &array[0]
,指针也可以用方框表达式去索引地址。因此还可以得到两个等式:
arr[i] == *(ar + 1)
&arr[i] == ar + 1
当然,只是视为指针,他们还有一些不同之处:
- sizeof(数组名)得到整个数组的长度
- &数组名,将得到整个数组的地址
数组用于参数调用
在函数定义中,数组的解释有一些新的变化:
- 在参数列表里,
int arr[]
和int *poi
等价,也就是说在参数列表里时,我们可以用指针代表数组,也可以直接使用数组名,代表传入一个数组的首元素地址,即指针指向的位置。 - 要注意,仅在函数头里可以等价使用,其他地方仍然不同于指针。
使用指针的优先级应当是最高的,毕竟数组拷贝与操作消耗资源更多,而使用指针访问地址更灵活高效。
数组其他操作
- 数组显示:用数组名/指针+方框表示法。
- 数组填充:因为参数调用时并没有创建数组副本,而只是传入数组地址,所以可以在函数里进行数组填充。
数组保护:为了保护数组元素,可以在形参声明处加入const
,例如void show(const int arr[],int size)
。但是要格外注意,const只是表示数组元素对指针是常量,也就是说不能通过指针来修改数组元素,但是我们可以不通过指针,直接对数组进行元素的修改,那是可行的。const只是保证不能通过指针修改数组。(有守门员就不能射门了么?天真)
那再想想,就是const指针可以指向常规变量和const变量,但是常规指针不能指向const变量。
当然,都是有转圜的余地的(待续)
这里再引入两种const的声明:
const int *arr
这是const指针,表示不能通过指针修改指向地址数据对象的值,即不能通过指针修改数组元素等。int* const arr
表示指针地址为常量const,即不能修改指针指向的地址,但是可以通过指针修改指向位置元素的值。
- 数组表示:除了上面’‘数组+长度’'的表示方式,我们还可以用两个指针表示区间。如
(int* arr,int* arr+size)
表示传入数组首地址和尾后一位地址(arr+size-1是数组最后一个元素地址)。
Tips:尽量使用const指针,避免函数调用可能对实参造成的数据篡改。
函数与结构
三种方式:
- 结构当作参数传递,会产生副本传入,所以如果结构过大资源消耗就会变多。
- 结构地址当作参数传入,要知道结构名只代表结构,和数组名不同,所以需要&来获得地址,用指针去取值。
- 引用变量。
第一种方式按值传递,传入结构。
polar rect_to_polar(rect xypos){
polar answer;
answer.distance =
sqrt( xypos.x * xypos.x + xypos.y * xypos.y);
answer.angle = atan2(xypos.y, xypos.x);
return answer; // returns a polar structure
}
第二种方式传入指针,参数定义为指针,也通过指针将函数处理返回结构。
void rect_to_polar(const rect * pxy, polar * pda){
pda->distance =
sqrt(pxy->x * pxy->x + pxy->y * pxy->y);
pda->angle = atan2(pxy->y, pxy->x);
}
rect_to_polar(&rplace, &pplace); //调用
函数与string对象
int main(){
string list[SIZE]; // an array holding 5 string object
cout << "Enter your " << SIZE << " favorite astronomical sights:\n";
for (int i = 0; i < SIZE; i++){
cout << i + 1 << ": ";
getline(cin,list[i]);
}
cout << "Your list:\n";
display(list, SIZE);
return 0;
}
void display(const string sa[], int n){
for (int i = 0; i < n; i++)
cout << i + 1 << ": " << sa[i] << endl;
}
关键就是声明一个string类型的数组,存放n个字符串。
函数指针
函数定义需要声明指针,那函数怎么作为指针呢
- 获取函数地址,函数名即函数地址
- 声明指针,只需要把待声明的函数名替换为(*pointer)即可
如:
double pam(int){
body;}
double (*poi)(int)即函数指针
poi = pam //赋地址
指针数组
const double* f1 (double ar[],int n);//函数原型
const double* (*poi1)(double [],int);//指针声明亦可初始化
auto poii = f1;//自动将poi声明为以上const double*指针
(*poi)(ar1,4);poii(ar1,4);//此时这两个函数调用等效,返回类型均为地址,如果需要获得函数返回值,则*(*poi)(ar1,4);
const double* (*poi[3])(double [],int)={f1,f2,f3};//包含三个指针的数组
//即三个指针,均指向需要double*和int参数且返回double*的几个函数,若要调用其中的函数,例如:
const double* px1 = poi[2](ar1,4);//此时px1即为调用ar1函数的指针
内联函数
函数调用两种方法:函数调用时跳转地址且参数入heap,调用完跳回原指令地址;内联函数直接复制副本,不执行地址跳转(亦按值传递,与常规函数无二)。
显然因为有复制副本的操作,所以一般只把代码量很小的函数作内联,且通常省略原型直接把函数定义在头部。
内联函数不能递归
实例:
inline double squa(double x){return x*x;}
引用变量
C++用&
来声明引用,注意在声明的时候&
不作为地址运算符,其他时候才是地址运算。
声明引用变量后,变量及它的引用变量指向同样的地址及值,即一个变量有两个名字。