C/C++ 进阶操作
概念基础
指针进阶
右左法则:首先需要牢记 优先级和结合性 的三大原则:
- 括号
()优先级最高(无论它在左边还是右边,代表改变优先级)。 - 后缀运算符(
[]数组下标、()函数调用)优先级次之,且从左向右结合。 - 前缀运算符(
*指针、&引用、const修饰符)优先级较低,且从右向左结合。 - 基本数据类型(如
int,char等)优先级最低。
正题开始:要从一个声明中得出正确的语义,有这样几个步骤:
- 找到标识符,如变量名、函数名
- 从标识符开始,向右看,如遇到
[或]就可以看作一个大小为 $N$ 的数组,元素类型是……;遇到(或)可以看作一个函数,接收的参数是……,返回值是……;遇到的是右括号),说明括号内解析完成,开始向左看~ - 遇到
type *,看作一个指针,指向……;遇到const或volatile在原来基础上,说且……是 只读/易变的 - 如果遇到左括号
(,说明又遇到了一个封闭的圆括号,调转方向,回到第二步向右看,继续解析。
// eg.1
int *p1[10]; // 这是数组
int (*p2)[10]; // 这是指针
// p1 标识符起点,向右为数组,向左数组元素为 (type *)类型
// p2 向右为 ),向左遇到 *,p2为指针,( 解析完毕,向右 [10] 为数组,向左说明数组为int类型
// eg.2
int (*func)(int,char);
// func 标识符,右向),左向 *,func为指针,跳出括号,(int,char) 指针指向一个函数,包含两个参数,左向int返回值
// eg.3
char *(*(*fp)[10])(int);
// fp 右遇括号,左侧即为fp类型为指针,跳出括号,[10]为数组,向左一个指针,说明指针指向一个10大小数组,跳出],右侧为 ), 向左测看,* 代表数组的元素为指针,左侧为 (,向右 ( 代表是一个函数,参数int类型,返回值是char *
// 总结(人话版):fp 是一个指针,指向10个大小的数组,数组元素为(指向一个int参数,返回值为char* 的函数)的指针
// c++ 的现代替代方案,使用using 或者 typedef 将类型封装隔离
// eg.4
int ( *( *getCallback() )[10])(int);
// c++ 推荐做法
using FuncPtr = int (*)(int); // 定义函数指针类型
using ArrayOfFuncPtrs = FuncPtr[10]; // 定义包含10个函数指针的数组
using ReturnType = ArrayOfFuncPtrs*; // 定义指向该数组的指针
ReturnType getCallback();
一些组合类型的例子:
(1)常量指针 / 指针常量 / 常量指针常量
// 常量指针:指向常量的指针
int a = 11,b = 23;
const int* p1 = &a;
p = &b; /* yes */ // *p = b; no
// 指针常量:是一个常量的指针
int const* p = &a;
*p = b; /* yes */ // p = &b; no
// 常量指针常量:指向常量的常量指针。
const int * const p = &a;
(2)指针数组 / 数组指针 / 指针数组指针
int *p[3]; // p 先和 [ ] 结合 → 本质是数组
int (*p)[3]; // p 先和 * 结合 → 本质是指针
int (*p[3])[4]; // p 先和 [] 结合 -> 本质是数组,接下来右左法则
// 结果:一个数组中3个指针分别指向一个包含4元素的数组,数组的值为int类型(本质是多维数组)
(3)指针函数 / 函数指针
int *fun(); // 本质是函数,无参,返回值为 int*
int (*p)(); // 本质是个指针,指向一个无参,返回值为 int 类型的函数
// 函数指针的应用:回调函数、函数作为参数传递、实现多态。
(4)指针结构体 / 结构体指针
struct Node {
int data; // 指针结构体:含有指针的结构体
Node *next; // 结构体成员是指针,传统链表,树,图的基本存储
};
Node *p = new Node(); // 本质是指针,一个指针指向结构体