复习函数基本知识:
要使用C++函数,要完成工作:
1. 提供函数基本知识;
2. 提供函数原型;
3. 调用函数
库函数是已经定义和编译好的函数,同时可以使用标准库头文件提供其原型
eg:标准头文件 cstring 中包含了 strlen() 和其它一些字符串相关的函数原型
函数原型和函数调用:
函数原型描述了函数到编译器的接口,将函数返回值的类型及参数类型和数量告诉编译器;
double cube ( double x ); or double cube ( double );
原型的功能:
编译器正确处理函数返回值;
编译器检查使用的参数数目是否正确;
编译器检查使用参数类型是否正确,如果不正确,转换为正确的类型(可能的话)
函数和数组:
int sum_arr ( int * arr,int n ) // 数组的元素为 int 型,因此 正确的函数头传递参数为 int * arr
int sum_arr ( int arr[ ],int n ) // 用 int arr[ ] 替换 int * arr ,含义相同。同时 int arr[ ] 还提醒用户 arr 不仅指向 int 而且是指向数组的第一个 int
在大多数情况下,C++和C语言一样,将数组名视为指针,存在两种例外:
1. 数组声明,使用数组名来标记存储位置;
2. 对数组名使用 sizeof 将得到整个数组的长度
函数传递数组时,将数组的位置(地址)、包含的元素种类(类型)以及元素的数目(n变量)提交给函数;
函数传递常规变量时,函数将使用变量的拷贝,但使用数组时,函数使用原来的数组;
可以在被传递函数中,数组参数使用 const 限定符,保证原始数组数据不被修改(只读传入)
int show_array ( const double arr[ ],int limit ); // 函数原型
键盘输入数组并显示输入数组例程:
1 #include<iostream> 2 using namespace std; 3 int fill_array(double ar[], int limit); 4 void show_array(const double ar[],int n); // const 可以防止修改数组内容 5 6 int main() 7 { 8 const int arsize = 5; 9 double ar[arsize]; 10 int n; 11 n = fill_array(ar, arsize); // 数组调用,将数组的首地址作为实参 12 //cout << n<<endl; 13 show_array(ar, n); 14 } 15 16 // 输入数组 17 int fill_array(double ar[], int limit) 18 { 19 double res; 20 int i; 21 for ( i = 0; i < limit; i++) 22 { 23 cout << "Enter value #" << i + 1 << ": "; 24 while (!(cin >> res)) // 判断是否正确读入数字,如果没有,进入循环 25 { 26 cin.clear(); // 重置输入,如果省略,程序将拒绝继续读取输入 27 while (cin.get() != ' ') // 清除输入流中所有的错误输入到' ' 28 continue; 29 cout << "wrong! please enter number: " 30 << "Enter value #" << i + 1 << ":"; 31 } 32 if (res < 0) // 负值输入将提前结束数组的输入 33 break; 34 ar[i] = res; 35 } 36 return i; 37 } 38 // 显示数组 39 void show_array(const double ar[], int n) 40 { 41 for (int i = 0; i < n; i++) 42 cout << ar[i] << endl; 43 }
自下而上的程序设计:
通过数据类型和设计适当的函数来处理数据,然后将这些函数组合成一个程序;
适合于OOP——它首先强调的数据表示和操纵
使用数组区间的函数:
对于处理数组的C++函数,必须将处理数组中的种类、数组的起始位置和数组中元素数量交给函数:
1. 将数组的起始处的指针作为一个参数,将数组的长度作为第二个参数;
2. 指定元素区间,通过传递两个指针完成,一个标识数组开头,一个标识数组尾部
eg:
sum = sum_arr ( arr, arr+3 ); // 函数调用,实参传入地址区间
int sum ( const int* begin,const int* end );// 函数头,形参为两个指向数组类型的指针(int*)
指针和 const:
可以使用两种不同的方式将 const 关键字用于指针:
1. 让指针指向一个常量对象,防止使用该指针修改所指向的值;
2. 将指针本身声明为常量
声明一个指向常量的指针:
int age = 39;
const int * pt = &age;
声明中 pt 指向一个 const int 因此不能使用 pt 修改这个值—— *pt 为常量不能修改
还可以将 const 变量的地址赋给指向const的指针,不能将 const 的地址赋给常规的指针
记住:如果数据类型不是指针,可以将 const 数据或非 const 数据的地址指向 const 指针
不能将 const 数据赋给非 const 指针
int * const finger = &sloth:
指针 finger 本身被声明为 const,使得 finger 只能指向 sloth,但允许使用 finger 来修改 sloth 的值
函数和二维数组:
数组作为参数的函数,数组名被视为地址,相应的形参应为一个指针;
int data[3][4] = { {1,2,3,4}, {9,8,7,6}, {2,4,6,8} }; // 声明
int tatal = sum(data, 3); // 调用
两种函数原型(形参的形式):
int sum (int (*ar2) [4],int size ); // 声明一个由4个指向 int 的指针组成的数组,括号不能省
int sum (int ar2[ ][4],int size); // 可读性更强
在函数定义中使用二维数组,最简单的办法是将 ar2 看作一个二维数组的名称
ar2 实际上是一个指针,必须对 ar2 执行两次解除引用才能得到数据:
最简单的方式:ar2[r][c];
ar2[r][c] = *(*(ar2 + r) + c); // same thing
函数和C-风格字符串:
将字符串作为参数时意味着传递的是地址;
表示字符串的方式有3种:
1. char 数组
2. 用引括号起的字符串常量
3. 被设置为字符串的地址的 char 指针
将字符串作为参数来传递,实际传递的是字符串的第一个字符的地址,形参声明应为 char*:
int c_in_str (const char * str,char ch); // 使用指针表示法
int c_in_str (const char str[],char ch); // 也可以使用数组表示法
处理字符串中字符的标准方式:
1 while (*str) // until *stt == ' ' 2 { 3 statement; 4 str++ // 将指针增加一个字节 5 }
返回C-风格字符串的函数:
函数无法返回一个字符串,但是可以返回字符串的地址;
char * buildstr ( char c,int n )
函数和结构:
为结构编写函数比为数组编写函数简单,结构变量相比于数组更接近于单值变量;
函数可以使用原始结构的拷贝,也可返回结构(因为结构可以互相赋值);
结构名只是结构的名称,要获得结构的地址,必须使用地址操作符 &;
处理结构的函数例程:
1 #include<iostream> 2 #include<cmath> 3 using namespace std; 4 5 struct polar { 6 double distance; 7 double angle; 8 }; 9 struct rect { 10 double x; 11 double y; 12 }; 13 polar rect_to_polar(rect xypose); 14 void show_polar(polar rrpose); 15 int main() 16 { 17 polar rppose; 18 rect xypose; 19 cout << "Enter pose x and y or type 'q' to quiz: "; 20 while (cin>>xypose.x>>xypose.y) 21 { 22 23 rppose = rect_to_polar(xypose); 24 show_polar(rppose); 25 cout << ("Continue Enter x and y or type 'q' to quiz: "); 26 27 } 28 return 0; 29 } 30 31 polar rect_to_polar(rect xypose) 32 { 33 polar rppose; 34 rppose.distance = sqrt(xypose.x*xypose.x + xypose.y*xypose.y); 35 rppose.angle = atan2(xypose.y, xypose.x); 36 return rppose; 37 } 38 39 void show_polar(polar rppose) 40 { 41 const double rad_to_deg = 57.29577951; 42 43 cout << "distance = " << rppose.distance << endl; 44 cout << "angle = " << rppose.angle*rad_to_deg << " degrees "; 45 46 }
传递结构地址例程:
与传递结构本身不同之处:
1. 调用函数时,将结构地址(&pplace)而不是结构本身(pplace);
2. 将形参声明为指向 polar 的指针,即 polar* 类型;
3. 形参指针而不是结构,因此应使用间接成员操作符(->)而不是(.);
4. 传递地址而不是复制,函数可能不在需要返回值,使用 void
1 #include<iostream> 2 #include<cmath> 3 using namespace std; 4 5 struct polar { 6 double distance; 7 double angle; 8 }; 9 struct rect { 10 double x; 11 double y; 12 }; 13 void rect_to_polar(const rect * xypose, polar* rppose); 14 void show_polar(const polar * rppose); 15 int main() 16 { 17 polar rppose; 18 rect xypose; 19 cout << "Enter pose x and y or type 'q' to quiz: "; 20 while (cin >> xypose.x >> xypose.y) 21 { 22 23 rect_to_polar( &xypose, &rppose ); 24 show_polar( &rppose ); 25 cout << ("Continue Enter x and y or type 'q' to quiz: "); 26 27 } 28 return 0; 29 } 30 31 void rect_to_polar(const rect* xypose, polar * rppose) 32 { 33 rppose->distance = sqrt(xypose->x*xypose->x + xypose->y*xypose->y); 34 rppose->angle = atan2(xypose->y, xypose->x); 35 } 36 37 void show_polar(const polar * rppose) 38 { 39 const double rad_to_deg = 57.29577951; 40 41 cout << "distance = " << rppose->distance << endl; 42 cout << "angle = " << rppose->angle*rad_to_deg << " degrees "; 43 44 }
函数和 string 对象:
C++如果需要多个字符串,可以声明一个 string 对象数组,而不是二维 char 数组;
以下例程声明了一个 string 对象数组,并将该数组传递给一个函数以显示其内容:
1 #include<iostream> 2 #include<string> 3 using namespace std; 4 const int SIZE = 5; 5 void display(const string list[], int n); 6 7 int main() 8 { 9 string list[SIZE]; // 声明 string 数组,每一个元素为一个 string 对象 10 cout << "Enter your " << SIZE << " favotite astronomical sights: "; 11 for (int i = 0; i < SIZE; i++) 12 { 13 cout << i + 1 << ": "; 14 getline(cin, list[i]); // 读取一个字符串 15 } 16 17 cout << "Your list: "; 18 display(list, SIZE); 19 20 return 0; 21 } 22 23 void display(const string list[], int n) 24 { 25 for (int i = 0; i < n; i++) 26 cout << list[i] << endl; 27 }
该例程中,除了函数 getline() 外,程序像对待内置类型(int)一样对待 string 对象
递归:
函数调用自己成为递归;
每个递归都创建自己的一套变量;
函数指针:
可以编写将另一个函数的地址作为参数的函数;
这种方法与调用函数相比,允许在不同的时间传递不同的函数地址;
函数指针基础知识:
获取函数地址;
声明函数的指针;
使用函数指针来调用函数
1. 获取函数地址
只要使用函数名即可,如果 think() 是一个函数,则 think 就是该函数的地址
要区分函数的地址和函数的返回值:
process ( think ); // 参数为函数地址,使得函数 process 能够在函数内部调用 think() 函数
thought ( think() ); // 参数为函数的返回值,先调用 think() 函数,其返回值传给 thought
2. 声明函数指针
声明函数指针时,必须指定指针指向的函数类型:
double pam ( int ); // 原型
double ( *pf ) ( int ); // 指针类型声明,将 pam 替换为了 (*pf),(*pf)是函数,pf 就是函数指针
pf = pam; // 将相应的函数地址赋给指针
提示: 要声明指向特定类型的函数指针,可以先编写这种函数的原型,然后用(*pf)替换函数名
注意: 函数地址赋给函数指针时,特征标和返回返回类型必须相同
3. 使用指针来调用函数
void estimate ( int lines,double (*pf) (int) ); // 函数原型
estimate ( 50, pam); // 让 estime() 使用 pam() 函数
(*pf) 扮演的角色与函数名相同,使用(*pf)时,只需要将它看成函数名
double pam ( int );
double (*pf) (int);
pf = pam; // 函数指针指向函数 pam()
double x = pam (4); // 使用函数名调用函数pam()
double y = (*pf) (5); // 使用函数指针调用函数pam()
double y = pf(5); // C++允许像使用函数名那样使用 pf
复习题:
函数 judge() 的返回值为 int,他将这样一个函数地址作为参数:将 const char 指针作为参数,返回一个 int 值,编写函数原型:
int judge ( int (*pf) ( const char * ) )