- 函数基本知识
- 函数原型
- 按值传递
- 处理数组的函数
- const指针参数
- 处理文本字符串的函数
- 处理结构的函数
- 处理string对象的函数
- 调用自身的函数(递归)
- 指向函数的指针
原型、调用、定义
7.1.1 定义函数- 无返回值return可选,有返回值return必须
- 返回值类型不能直接为数组
- 可以将数组作为结构或对象组成部分间接返回
- 为什么需要原型? 事先打个招呼,不致于太陌生
- 原型参数列表中,可以包括变量名,也可以不包括(饭店订餐问几人几点到,而不会问每个人姓名)
- 如果函数的两个参数类型相同,必须为每个参数指定类型
void fifi(float a, float b) void fufu(float a, b) //NOT acceptable
//程序清单7.3 //函数有两个参数 #includeusing namespace std; void n_chars(char c, int n); int main(void) { char ch; int times; cout << "Enter a character: "; cin >> ch; while (ch != 'q') { cout << "Enter an integer: "; cin >> times; n_chars(ch, times); cout << "nEnter another character or press the q-key to quit: "; cin >> ch; } cout << "The value of times is " << times << ".n"; return 0; } void n_chars(char c, int n) { while (n-- > 0) cout << c; }
- ch是字符,字符与数字混合输入时,cin>>ch可以跳过换行符
- ch是字符串时,字符串与数字混合输入,cin.get(ch),注意消耗换行符
while(cin.get() != 'n') continue;
//程序清单7.5 //函数与一维数组 #includeusing namespace std; const int ArSize = 8; int sum_arr(int arr[], int n); int main(void) { int cookies[ArSize] = {1, 2, 4, 8, 16, 32, 64, 128}; int sum = sum_arr(cookies, ArSize); cout << "Total cookies eaten: " << sum << endl; return 0; } int sum_arr(int arr[], int n) { int total = 0; for (int i = 0; i < n; i++) total += arr[i]; return total; }
- C++将数组名解释为其第一个元素的地址:cookies == &cookies[0]
- 该规则有一些例外。首先数组声明使用数组名标记存储位置;其次对数组名使用sizeof得到整个数组的长度;第三地址运算符&用于数组名时返回整个数组的地址
int sum_arr(int arr[], int n) //数组表示法 int sum_arr(int * arr, int n) //指针表示法 //int arr[] 标识起始位置 //int n 标识整个数组在内存中的地址范围 arr[i] == *(arr + i) //arr指针加i,取出值就是数组第i个元素的值 &arr[i] == arr + i //arr指针加i,就是数组第i个元素的地址 int sum = sum_arr(cookies, ArSize) //返回整个数组的和 sum = sum_arr(cookies, 3) //数组前3个元素的和 sum = sum_arr(cookies + 4, 4) //数组后4个元素的和7.3.3 更多数组示例
//程序清单7.7 //数组函数与const #include7.3.4 使用数组区间的函数using namespace std; const int Max = 5; int fill_array(double ar[], int limit); void show_array(const double ar[], int n); void revalue(double r, double ar[], int n); int main(void) { double properties[Max]; int size = fill_array(properties, Max); show_array(properties, size); if (size > 0) { cout << "Enter revaluation factor: "; double factor; while (!(cin >> factor)) //错误输入清理专用 { cin.clear(); //清除错误标识位 while (cin.get() != 'n') continue; //消耗缓冲区所有内容 cout << "Bad input; Please enter a number: "; } revalue(factor, properties, size); show_array(properties, size); } cout << "Done.n"; cin.get(); cin.get(); return 0; } int fill_array(double ar[], int limit) { double temp; int i; for (i = 0; i < limit; i++) { cout << "Enter value #" << (i + 1) << ": "; cin >> temp; if (!cin) //错误输入清理专用 { cin.clear(); while (cin.get() != 'n') continue; cout << "Bad input; Please enter a number: "; break; } else if (temp < 0) break; ar[i] = temp; // else // ar[i] = temp; } return i; //成功录入的数量 } void show_array(const double ar[], int n) //不希望通过指针修改指向对象内容 { for (int i = 0; i < n; i++) { cout << "Property #" << (i + 1) << ": $"; cout << ar[i] << endl; } } void revalue(double r, double ar[], int n) { for (int i = 0; i < n; i++) ar[i] *= r; }
- 指定元素区间,可以通过传递两个指针完成:一个指针标识数组开头,另一个指针标识数组尾部。
//程序清单7.8 #include7.3.5 指针和constusing namespace std; const int ArSize = 8; int sum_arr(const int* begin, const int* end); int main(void) { int cookies[ArSize] = { 1,2,4,8,16,32,34,128 }; int sum = sum_arr(cookies, cookies + ArSize); cout << "Total cookies eaten: " << sum << endl; sum = sum_arr(cookies, cookies + 3); cout << "First three eaten: " << sum << endl; sum = sum_arr(cookies + 4, cookies + 8); cout << "Last four eaten: " << sum << endl; return 0; } int sum_arr(const int* begin, const int* end) { const int * pt; int total = 0; for (pt = begin; pt != end; pt++) total += *pt; return total; }
- const int * pt == int const * pt
- 指针指向常量对象,防止使用指针来修改所指向的值
- int * const pt
- 将指针本身声明为常量,防止改变指针指向的位置
- const int * const pt
- 既不允许使用指针修改指向的值,也不允许指针更换指向对象
int data[3][4] = {{1,2,3,4}, {9,8,7,6}, {2,4,6,8}};
int total = sum(data, 3);
//sum的原型
int sum(int (*ar2)[4], int size);
//指向4个int组成数组的指针
//等于
int sum(int ar2[][4], int size);
//而不是
int sum(int *ar2[4], int size);
//这是4个int*指针组成的数组
//另外函数参数不能是数组
ar2[r][c] == *(*(ar2 + r) + c)
ar2 //指向 4 个 int 数组的第一行的指针
ar2 + r //指向第 r 行的指针(4 个 int 的数组)
*(ar2 + r) //第 r 行
*(ar2 + r) + c //指向第 r 行 第 c 列的指针
*(*(ar2 + r) + c) //第 r 行 第 c 列
7.5 函数和C—风格字符串
7.5.1 将C—风格字符串作为参数的函数
三种表示字符串的方式:
- char数组
- 引号括起的字符串常量(字符串字面值)
- 被设置为字符串地址的char指针(char*)
//程序清单7.9 //函数和字符串 #include7.5.2 返回C—风格字符串的函数using namespace std; //unsigned int c_in_str(const char* str, char ch); //指针表示法 unsigned int c_in_str(const char str[], char ch); //数组表示法 int main(void) { char mmm[15] = "minimum"; const char* wail = "ululate"; //字符指针 // char* wail = (char*)"ululate"; //类型不匹配,加const或强制类型转换 unsigned int ms = c_in_str(mmm, 'm'); unsigned int us = c_in_str(wail, 'u'); cout << ms << " m characters in " << mmm << endl; cout << us << " u characters in " << wail << endl; return 0; } //unsigned int c_in_str(const char* str, char ch) unsigned int c_in_str(const char str[], char ch) { unsigned int count = 0; while (*str) //字符串常量以 结尾 { if (*str == ch) count++; str++; //将指针移动到下一个字符 } return count; }
函数无法返回一个字符串,但可以返回字符串的地址,而且效率更高
//程序清单7.10 #include7.6 函数和结构 7.6.1 传递和返回结构using namespace std; char* buildstr(char c, int n); int main(void) { int times; char ch; cout << "Enter a character: "; cin >> ch; cout << "Enter an integer: "; cin >> times; char * ps = buildstr(ch, times); cout << ps << endl; delete[] ps; return 0; } char* buildstr(char c, int n) { char* pstr = new char[n + 1]; pstr[n] = ' '; while (n-- > 0) pstr[n] = c; //for (int i = 0; i < n; i++) // pstr[i] = c; return pstr; }
//程序清单7.11 //函数与结构 #include7.6.2 另一个处理结构的函数using namespace std; struct travel_time { int hours; int mins; }; const int Mins_per_Hr = 60; travel_time sum(travel_time t1, travel_time t2); void show_time(travel_time t); int main(void) { travel_time day1 = {5, 45}; travel_time day2 = {4, 55}; travel_time trip = sum(day1, day2); cout << "Two-day total: "; show_time(trip); travel_time day3 = {4, 32}; cout << "Three-day total: "; show_time(sum(trip, day3)); return 0; } travel_time sum(travel_time t1, travel_time t2) { travel_time total; total.mins = (t1.mins + t2.mins) % Mins_per_Hr; total.hours = t1.hours + t2.hours + (t1.mins + t2.mins) / Mins_per_Hr; return total; } void show_time(travel_time t) { cout << t.hours << " hours, " << t.mins << " minutesn"; }
//程序清单7.12 #include7.6.3 传递结构的地址#include using namespace std; struct polar { double distance; double angle; }; struct rect { double x; double y; }; polar rect_to_polar(rect xypos); void show_polar(polar dapos); int main(void) { rect rplace; polar pplace; cout << "Enter the x and y values: "; while (cin >> rplace.x >> rplace.y) { pplace = rect_to_polar(rplace); show_polar(pplace); cout << "Next two numbers (q to quit): "; } cout << "Done!n"; return 0; } 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; } void show_polar(polar dapos) { const double Rad_to_Deg = 57.29577591; cout << "distance = " << dapos.distance << ", angle = " << dapos.angle * Rad_to_Deg << " degreesn"; }
节省时间和空间
- 调用函数时,将结构的地址(&pplace)而不是结构本身(pplace)传递给它
- 将形参声明为指向polar的指针,即polar*类型。由于函数不应该修改结构,故使用const
- 由于形参是指针而不是结构,因此使用间接成员运算符(->),而不是成员运算符(.)
//程序清单7.13 #include#include using namespace std; struct polar { double distance; double angle; }; struct rect { double x; double y; }; void rect_to_polar(const rect* pxy, polar* pda); void show_polar(const polar* pda); int main(void) { rect rplace; polar pplace; cout << "Enter the x and y values: "; while (cin >> rplace.x >> rplace.y) { rect_to_polar(&rplace, &pplace); show_polar(&pplace); cout << "Next two numbers (q to quit): "; } cout << "Done!n"; return 0; } 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); } void show_polar(const polar * pda) { const double Rad_to_Deg = 57.29577591; cout << "distance = " << pda->distance << ", angle = " << pda->angle * Rad_to_Deg << " degreesn"; }
程序清单7.12和7.13之间的差别在于,7.12使用结构副本,7.13使用指针,指针能够对原始结构进行操作,效率更高,消耗内存更少
7.7 函数和string对象//程序清单7.14 #include7.8 函数和array对象#include using namespace std; const int SIZE = 5; void display(const string sa[], int n); int main(void) { string list[SIZE]; 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; }
//程序清单7.15 #include7.9递归 7.9.1包含一个递归调用的递归#include #include using namespace std; const int Seasons = 4; //array arr const array Snames = { "Spring", "Summer", "Fall", "Winter" }; void fill(array * pa); void show(array da); int main(void) { array expenses; fill(&expenses); //不能写成fill(expense),此时fill修改expense副本,应该用指针 show(expenses); return 0; } void fill(array * pa) { for (int i = 0; i < Seasons; i++) { cout << "Enter " << Snames[i] << " expense: "; cin >> (*pa)[i]; //cin >> pa[i] 不能这样写, pa是array对象的指针,并不是对象 //cin >> *pa[i] 也不能这样写,[]优先级高于* } } void show(array da) { double total = 0; cout << "nEXPENSEn"; for (int i = 0; i < Seasons; i++) { cout << Snames[i] << ": $" << da[i] << endl; total += da[i]; } cout << "Total Expense: $" << total << endl; }
//程序清单7.16 #include7.9.2 包含多个递归调用的递归using namespace std; void countdown(int n); int main(void) { countdown(4); return 0; } void countdown(int n) { //Countdown ... 按函数调用顺序执行 cout << "Countdown ... " << n << " (n at " << &n << ")" << endl; if (n > 0) //执行条件 countdown(n - 1); //更新条件 //Kaboom: 按函数调用相反顺序执行 cout << "Kaboom: " << n << " (n at " << &n << ")" << endl; } //每个递归调用创建新变量,地址都不同
//程序清单7.17 #include7.10 函数指针 7.10.1 函数指针的基础知识using namespace std; const int Len = 66; const int Divs = 6; void subdivide(char ar[], int low, int high, int level); int main(void) { char ruler[Len]; //字符数组 for (int i = 0; i < Len - 2; i++) ruler[i] = ' '; //清空 ruler[Len - 1] = ' '; //避免出现乱码 int max = Len - 2; int min = 0; ruler[min] = ruler[max] = '|'; cout << ruler << endl; for (int i = 1; i <= Divs; i++) { subdivide(ruler, min, max, i); cout << ruler << endl; for (int j = 1; j < Len - 2; j++) ruler[j] = ' '; } return 0; } void subdivide(char ar[], int low, int high, int level) //void subdivide(char *ar, int low, int high, int level) { if (level == 0) //循环条件 return; int mid = (high + low) / 2; ar[mid] = '|'; subdivide(ar, low, mid, level - 1); //level - 1 更新条件 subdivide(ar, mid, high, level - 1); }
- 获取函数地址
如果think()是函数,则think就是该函数地址。一定要区分传递的是函数地址还是函数返回值
process(think) //函数地址 thought(think()) //函数返回值
- 声明函数指针
double pam(int); //函数声明 double (*pf)(int); //函数指针声明
pf是这类函数的指针,必须在声明中将(*pf)括起。括号优先级高于*
double *pf(int) //*pf(int)代表pf()是返回指针的函数 double (*pf)(int) //(*pf)(int)代表pf是指向函数的指针
- 使用函数指针调用函数
使用(*pf)时,将之看做函数名即可
double pam(int); double (*pf)(int); pf = pam; double x = pam(4); double y = (*pf)(5); //实际上,C++也允许像使用函数名那样使用pf //pf 和 (*pf)等价 double y = pf(5);7.10.2 函数指针示例
//程序清单7.18 #include7.10.3 深入探讨函数指针using namespace std; double betsy(int lns); void estimate(int lines, double (*pf)(int)); int main(void) { int code; cout << "How many lines of code do you need? "; cin >> code; cout << "Here's Betsy's estimate:n"; estimate(code, betsy); return 0; } double betsy(int lns) { return 0.05 * lns; } void estimate(int lines, double (*pf)(int)) { cout << lines << " lines will take " << (*pf)(lines) << " second(s)n"; //cout << lines << " lines will take " << pf(lines) << " second(s)n"; }
//程序清单7.19 #includeusing namespace std; const double* f1(const double ar[], int n); const double* f2(const double [], int n); const double* f3(const double *, int n); int main(void) { double av[3] = { 1112.3, 1542.6, 2227.9 }; //PART 1 //函数指针 const double* (*p1)(const double*, int) = f1; //函数指针(函数名)赋给函数指针 auto p2 = f2; // const double* (*p2)(const double*, int) = f2; cout << "Using pointers to functions:n"; cout << "Adress Valuen"; //函数返回值地址 : 函数返回值 cout << (*p1)(av, 3) << ": " << *(*p1)(av, 3) << endl; //(*p1)(av, 3) == f1(av, 3) cout << p2(av, 3) << ": " << *p2(av, 3) << endl; //p2(av, 3) == f2(av, 3) //PART 2 //指针数组 //pa/pb是包含3个函数指针的数组 const double* (*pa[3])(const double*, int) = { f1, f2, f3 }; //数组名是指向第一个元素的指针,pa和pb都是指向函数指针的指针 auto pb = pa; // const double* (**pb)(const double*, int) = pa; cout << "nUsing an array of pointers to functions:n"; cout << "Adress Valuen"; for (int i = 0; i < 3; i++) cout << pa[i](av, 3) << ": " << *pa[i](av, 3) << endl; cout << "nUsing a pointer to a pointer a function:n"; cout << "Adress Valuen"; for (int i = 0; i < 3; i++) cout << pb[i](av, 3) << ": " << *pb[i](av, 3) << endl; //PART 3 //函数指针数组的指针 cout << "nUsing pointers to an array of pointersn"; cout << "Adress Valuen"; //pc指向整个数组的指针 auto pc = &pa; // const double* *(*pc)[3](const double*, int) = &pa; cout << (*pc)[0](av, 3) << ": " << *(*pc)[0](av, 3) << endl; //(*pc)[0](av, 3) == pa[0](av, 3) == f1(av, 3) //pd是指针,指向包含3个元素的数组 const double* (*(*pd)[3])(const double*, int) = &pa; const double* pdb = (*pd)[1](av, 3); // (*pd)[1](av, 3) == pa[1](av, 3) == f2(av, 3) -->pdb指向f2(av, 3) cout << pdb << ":" << *pdb << endl; cout << (*(*pd)[2])(av, 3) << ": " << *(*(*pd)[2])(av, 3) << endl; // cout << (*pd)[2](av, 3) << ": " << *(*pd)[2](av, 3) << endl; // (*(*pd)[2])(av, 3) == (*pa[2])(av, 3) == f3(av, 3) return 0; } //pd指向数组,*pd就是数组,(*pd)[i]是数组中的元素,即函数指针 //函数调用是(*pd)[i](av,3),*(*pd)[i](av,3)是返回的指针指向的值 //或者调用(*(*pd)[i])(av,3),*(*(*pd)[i])(av,3)是指向double的值 const double* f1(const double* ar, int n) { return ar; //数组名,第一个元素地址 } const double* f2(const double ar[], int n) { return ar + 1; //第二个元素地址 } const double* f3(const double ar[], int n) { return ar + 2; //第三个元素地址 }
这个代码学的不够清楚,再看看
注意括号优先级:
- (*pa[2])(av, 3) == f3(av, 3) 第三个元素的地址
- *pa[2](av, 3) == *f3(av, 3) 第三个元素的值
typedef double real //real 成了double的别名
typedef const double* (*p_fun)(const double*, int); //p_fun是函数指针别名
p_fun p1 = f1; //p1指向函数f1
p_fun pa[3] = {f1, f2, f3}; //pa 3 个函数指针的数组
p_fun (*pd)[3] = &pa; //pd 指向一个由 3 个函数指针组成的数组
用定义变量的方式,加上typedef,变量就变成类型别名



