导图社区 C++第7章:函数C的编程模块
《C Primer Plus》第七章同步思维导图。讲解了C 的编程模块,包括函数体、结构等等,多为C 与C的通用部分。另外加入了函数指针的介绍,难度适中,适合巩固复习。
编辑于2021-01-24 01:30:46第7章 函数——C++的编程模块
7.1 复习函数的基本知识
7.1.1 定义函数
7.1.2 函数原型和函数调用
1. 为什么需要函数原型
2. 原型的语法
3. 原型的功能
· 编译器正确处理函数返回值 · 编译器检查使用的参数数目是否正确 · 编译器检查使用的参数类型是否正确
7.2 函数参数和值传递
用于接收传递值的变量被称为形参,传递给函数的值被称为实参。C++标准 使用参数(argument)来表示实参,使用参量(parameter)来表示形参。
7.2.1 多个参数
7.2.2 另一个接收两个参数的函数
7.3 函数和数组
7.3.1 函数如何使用指针来处理数组
C++将数组名解释为第一个元素的地址: arr == &arr[0]; 但数组名也不仅仅只有第一个元素的地址的含义。如, · 数组声明使用数组名来标记储存位置; · 对数组名使用sizeof运算符将得到整个数组的长度(以字节为单位) · 将地址运算符用于数组名时,将返回整个数组的地址(假设数据类型是int,数组长度为8,则返回一个32字节的内存块地址)
7.3.2 将数组作为参数意味着什么
注: 将数组类型和元素数量告诉数组处理函数,请通过两个不同的参数来处理他们: void fillArray(int arr[], int size); // prototype 而不要试图使用方括号表示法来传递数组长度: void fillArray(int arr[size]); // No -- bad prototype
7.3.3 更多数组函数示例
1. 填充数组
2. 显示数组及用const保护数组
3. 修改数组
4. 将上述代码结合起来
5. 程序说明
6. 数组处理函数的常用编写方式
7.3.4 使用数组区间的函数
7.3.5 指针和const
尽可能使用const: · 可以避免由于无意间修改数据而导致的编程错误 · 使用const使得函数能够处理const和非const实参,否则只能接受非const数据 · 如果条件允许,应将指针形参声明为指向const的指针
(1)常规变量的地址赋给指向const的指针: int age = 39; const int* pt = &age; 这里pt的声明不意味着它指向的值是一个常量,而是对pt而言,这个值是常量,pt指向age,而age不是常量。可以通过age修改这个值,但是不能通过*pt修改。
(3)C++不允许将const变量的地址赋给常规指针: const float g_moon = 1.63; float *pm = &g_moon; // INVALID
(2)const变量的地址赋给指向const的指针: const float g_earth = 9.8880; const float* pe = &g_earth; 对于这种情况,既不能使用g_earth来修改值9.80,也不能用*pe修改
const修饰指针
(1) int age = 39; const int* p = &age; 这里只能通过age来改变值,而不能通过*p来改变,const的声明表示了p所指向的值对p来说是个常量(即*p是常量)
(2) int sloth = 3; int* const finger = &sloth; 这里可以使用*finger来改变sloth的值,但不同的是,finger只能指向sloth,不能指向其他地址。const的声明表示了finger指针是个常量,不能改变指针的地址,但是可以改变它指向的值(即finger是常量)
(3) double trouble = 2.0E30; const double* const stick = &trouble; 这里stick啥也干不了,只能显示trouble的值,但可以通过trouble来改变trouble的值(即stick和*stick都是常量)
7.4 函数和二维数组
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); int sum(int ar2[][4], int size); 其中,指针类型指出,它指向由4个int组成的数组,因此指定了列数,无需通过新增一个参数来表示列数。同时,因为没有指定行数,因此对行数没有限制。
7.10 函数指针
7.10.1 函数指针的基础知识
1. 获取函数的地址
获取方式:使用函数名(后面不跟参数) 如果think()是一个函数,那么think就是这个函数的地址: process(think); // passes address of think() to process thought(think()); // passes return value of think() to thought() process()调用使得process()函数能够在其内部调用think()函数,而thought()调用首先调用think()函数,然后将其返回值传给thought()函数
2. 声明函数指针
double pam(int); // prototype 正确的指针类型声明如下: double (*pf)(int); 这与pam()声明类似,只是将pam替换为了(*pf)。由于pam是函数,因此(*pf)也是函数,而pf就是函数指针。 正确声明pf后,记得还需给它赋值相应的函数: double pam(int); double (*pf)(int); pf = pam; 注意,pam()的特征标(即参数列表)和返回类型必须与pf相同,否则编译器将拒绝这种赋值
3. 使用指针来调用函数
使用(*pf)时,只需将它看做函数名即可: double pam(int); double (*pf)(int); pf = pam; double x = pam(4); double y = (*pf)(5); 实际上,C++也允许像使用函数名一样使用pf: double y = pf(5); 虽然第一种格式不太好看,但是它给出了强有力的提示——代码正在使用函数指针,而不是定义了一个pf()的函数
7.10.2 函数指针示例
7.10.3 深入探讨函数指针
下面是一些函数的原型,它们的特征标和返回类型相同: const double* f1(const double ar[], int n); const double* f2(const double [], int); const double* f3(const double*, int); 可以在声明的同时进行初始化: const double* (*p1)(const double*, int) = f1; 使用C++自动类型推断功能时,代码要简单的多: auto p2 = f2; 当然还可以使用函数指针数组: const double* (*pa[3])(const double*, int) = {f1, f2, f3}; 这里不能使用auto,因为自动类型推断只能用于单值初始化,不能用于列表初始化。但是声明数组pa后,声明同样的数组可以用auto: auto pb = pa;
7.10.4 用typedef进行简化
7.9 递归
7.9.1 包含一个递归调用的递归
7.9.2 包含多个递归调用的递归
7.7 函数和string对象(*)
7.6 函数和结构
7.6.1 传递和返回结构
结构可以像基本类型一样,作为参数传递,并可以作为函数的返回值返回。 当结构比较小时,按值传递结构最合理
7.6.2 另一个处理结构的示例
7.6.3 传递结构的地址
7.5 函数和C-风格字符串
7.5.1 将C-风格字符串作为参数的函数
三种字符串表示方式: · char数组 · 用引号括起的字符串常量 · 被设置为字符串的地址的指针
字符串处理的标准方式: while (*str) { statement(s) str++; }
7.5.2 返回C-风格字符串的函数(返回指针)