CPP 7. 函数--C++的编程模块 2020-10-17 浏览量 567 暂无评论 # 第7章 函数--C++的编程模块 ## 7.1 自定义函数 使用自定义函数:定义、提供原型、调用。 🐸小知识: 1. 如果声明的返回类型为double,而函数返回一个int表达式,则该int值将被强制转换为double类型; 2. C++的返回值类型可以是除数组外的其他任何类型,如整数、浮点数、指针,甚至可以是结构和对象(C++函数虽然不能直接返回数组,但可以将数组作为结构或对象组成部分来返回); 3. 函数通过将返回值复制到指定的CPU寄存器或内存单元中来将其返回,随后,调用程序将查看该内存单元; 4. 如果函数包含多条返回语句,则函数在执行遇到的第一条返回语句后结束; ### 7.1.1 为什么需要原型 原型描述了函数到编译器的接口,它将函数返回值的类型以及参数的类型和数量告诉编译器。 ### 7.1.2 原型的语法 ```cpp double cube(double x); void cheers(int); void say_hi(); // 函数没有参数 void say_bye(...); // 不指定参数列表(接受可变参数) ``` 函数原型不要求提供变量名,有类型列表就够了,原型中的变量名相当于占位符,因此不必与函数定义中的变量名相同。 ## 7.2 函数参数和按值传递 实参(参数argument):传递给函数的值; 形参(参量parameter):用于接收传递值得变量; 🐸小知识: 1. 如果函数的两个参数类型相同,则必须分别指定每个参数的类型; 2. 函数内定义的变量是局部变量; ```cpp // 交替进行乘除的策略 long double probability(unsigned numbers, unsigned picks) { long double result = 1.0; long double n; // 为什么要将n声明为long double呢? unsigned p; for (n = numbers, p = picks; p > 0; n--, p--) result = result * n / p; return result; } ``` ## 7.3 函数和数组 🐸小知识: 1. 当且仅当用于函数头或函数原型中,`int * arr和int arr[]`的含义才是相同的; 2. 传递常规变量时,函数将使用该变量的拷贝,但传递数组时,函数将使用原来的数组(因为传递的其实是一个地址,这也是为什么传递数组名的同时还要传递长度); 3. 可在声明形参时使用关键字const防止函数无意中修改数组的内容; ```cpp void f_modify(double ar[], int n); void _f_no_change(const double ar[], int n); ``` ```cpp #include const int ArSize = 8; int sum_arr(int arr[], int n); int main() { using namespace std; int cookies[ArSize] = {1, 2, 4, 8, 16, 32, 64, 128}; int sum = sum_arr(cookies, ArSize); cout << "Total cookied eaten: " << sum << endl; cin.get(); return 0; } int sum_arr(int arr[], int n) // int sum_arr(int * arr, int n) { int total = 0; for (int i = 0; i < n; i++) total = total + arr[i]; return total; } ``` ### 7.3.1 指针和const 🐸小知识: 1. 指向const的指针,不能通过指针修改其所指向的值;指针本身是const,不能修改指针指向的位置; 2. 尽可能指针参数声明为指向常量数据的指针,因为: a. 这样可以避免由于无意间修改数据而导致的编程错误; b. 使用const使得函数能够处理const和非const实参,否则将只能接受非const数据。 ```cpp int age = 23; //pt指向一个const int,即*pt的值为const const int * pt = &age; // 可以通过age修改age,但不能通过pt修改age const float g_earth = 9.80; const float * pe = &g_earth; // valid float * pm = &g_earth; // invalid float * const pn = &g_earth; // 指针本身是const,不能修改,但可使用pn修改g_earth的值 // 简单来说:pn和*pe是const,而*pn和pe不是 ``` 🌈难点: 仅当只有一成间接关系(如指针指向基本数据类型)时,才可以将非const地址或指针赋给const指针。 ```cpp const int **p2; int *p1; const int n =13; pp2 = &p1; // not allowed, but suppose it were *pp2 = &n; // valid, both const, but sets p1 to point at n *p1 = 10; // valid, but change const n ``` ## 7.4 函数和二维数组 原型:`int sum(int (*ar2)[4], int size);`,只能接受由4列组成的数组,行数可变,等价于`int sum(int ar2[][4], int size);` ## 7.5 函数和C-风格字符串 C-风格字符串由一系列字符组成,以空值字符结尾。(包含字符,但不以空值字符结尾的char数组只是数组,而不是字符串,因此不必将字符串长度作为参数传递给函数) ```cpp // 处理字符串中字符的标准方式 while (*str) { statements str++; } #include char * buildstr(char c, int n); int main() { using namespace std; 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; cin.get(); cin.get(); return 0; } char * buildstr(char c, int n) { char * pstr = new char[n+1]; pstr[n] = '\0'; while(n--) pstr[n] = c; return pstr; } ``` 注意: 变量pstr的作用域为buildstr函数内,因此当该函数结束时,pstr使用的内存将被释放,但new新建的字符串还在,由于函数返回了pstr的值,因此程序仍可以通过main()中的指针ps来访问新建的字符串。 ## 7.6 函数和结构 结构可使用按值传递,但是如果结构非常大,按值传递将增大内存要求,降低系统运行的速度,所以出现了另外两种方式:传递结构的地址和按引用传递。 ```cpp // 输入两个数字 int x, y; while (cin >> x >> y) { statements ... } ``` ## 7.7 函数和string、array对象 在C++中,类对象是基于结构的。 ## 7.8 递归 在需要将一项工作分为两项较小的、类似的工作时,递归非常有用。 递归方法有时被称为分而治之策略。 ## 7.9 函数指针 函数的地址:函数名 声明函数指针: ```cpp double pam(int); double (*pf)(int); pf = pam; double x = pam(4); double y = (*pf)(4); double z = pf(4); // 函数指针数组 const double * (*pa[3])(const double *, int) = {f1, f2, f3}; // auto只能用于单值推断 // 使用typedef简化声明 typedef const double *(*p_fun)(const double *, int); p_fun p1 = f1; p_fun p3 = {f1, f2, f3}; ``` 赞赏 微信支付 支付宝支付