写在前面:我写博客主要是为了对知识点的总结、回顾和思考,把每篇博客写得通俗易懂 是我的目标,因为能让别人看懂,才是真的学会了
从Math到CS的跨专业经历,让我格外珍惜学习时间,更加虚心好学,而分享技术和知识是快乐 的,非常欢迎大家和我一起交流学习,期待与您的进一步交流
背景
在学习C++指针时,我们经常会遇到一些比较复杂的指针类型定义,很多人可能一直是一知半解的,比如下面这11个类型,你能否分析清楚p是什么类型
1:int p;
2:int *p;
3:int p[4];
4:int *p[4];
5:int (*p)[4];
6:int **p;
7:int p(int); // 函数声明
8:int *p(int); // 函数声明
9:int (*p)(int); // 函数指针
10:int *(*p(int))[4];
11:const int * const p; // 指向常量的常量指针
那下面我会一个个详细地分析,这样以后再遇到指针类型分析,就不用害怕了
复杂类型分析原则
从变量名开始,根据运算优先级结合,一步一步分析
- 如果是指针,下一步就考虑指向什么
- 如果是数组,下一步就考虑元素大小是多少,元素类型是什么
- 如果是函数,下一步就考虑参数是什么类型,返回值类型是什么
基本上掌握了这个原则,90%的复杂类型你都能分析得明明白白
例1
int p;
这是一个普通的整型变量
例2
int *p;
我们从变量名p开始分析,与它结合的是*所以p是一个指针,那接下来就是指针指向什么了,与int结合,说明指针所指向的内容是int型的,因此p是一个指向整型数据的指针
例3
int p[4];
从变量名p开始,先与[],表明p是一个数组,元素个数是4,接下来就是数组的元素是什么类型了,与int结合,说明元素的类型是整型,因此p是一个由整型数据组成的数组
例4
int *p[4];
从变量名p开始,因为[]
优先级比*
高,所以p先与[]结合说明p是个数组,元素个数是4,接下来就是数组的元素是什么类型了,与*结合,说明元素的类型是指针,接下来就是指针指向什么了,与int结合说明指针指向int型数据,因此p是一个由指向int型指针组成的数组
例5
int (*p)[4];
从变量名p开始,因为()
的存在,所以会先与*
结合,而不是[],说明p是一个指针,接下来就是指针指向什么了,与[]结合说明指针指向一个数组,元素大小是4,接下来就是数组的元素是什么类型了,与int结合,说明元素的类型是整型,因此p是一个指向由int型数据组成的数组的指针
例6
int **p;
从变量名p开始,先与*
结合,说明p是一个指针,接下来就是指针指向什么了,与*集合说明指针p指向的元素是指针,然后与int结合,说明这个指针指向int型数据,因此p是一个指向指针(指向int型数据)的指针
例7
int p(int);
从变量名p开始,与()结合,说明p是一个函数,函数的参数是int,接下来就是函数的返回值类型是什么了,与int结合,说明返回值类型是int,因此p是一个函数,参数是int,返回值类型是int
例8
int *p(int);
从变量名p开始,由于优先级会先与()结合,则p是个函数,参数是int,接下来就是函数的返回值类型是什么了,与*集合说明是一个指针,然后与int结合,说明指针指向int,因此p 是一个函数,参数是int,返回值类型是一个指向int型数据的指针
例9
int (*p)(int);
从变量名p开始,由于优先级会先与*结合(第一个()就是来改变优先级的),说明p是一个指针,接下来就看指针指向什么,与()结合说明指针p指向一个函数,函数的参数是int,返回值是int,因此p是一个指向有一个整数参数且返回类型是整型的函数的指针(函数指针)
例10
int *(*p(int))[4];
从变量名p开始,与()结合说明p是一个函数,参数是int,再与*
结合说明返回值是一个指针,然后再与[]结合,说明指针指向一个数组,元素大小是4,与*结合说明元素类型是指针,最后与int结合说明指针指向int型,因此p是一个参数为int型数据,返回值为一个指向由int型指针组成的数组的函数
例11
const int * const p; // 指向常量的常量指针
从变量名p开始,const说明p是一个常量对象,然后和*结合说明p是个指针,因此p是个常量指针,然后再与int结合说明,指针指向的是int对象,最后和const结合说明这个int对象是常量对象,因此p是指向int型常量的常量指针
更多解释请看
【C++ const引用和const指针详解】 (const int *p;) (int const *p;) (int *const p;)三者的区别
补充:定义变量的同时赋值
int a = 3;
int *p; // 定义
p = &a // 赋值
// 等价于int *p = &a; // 定义的同时赋值
上面的代码其实很简单,之所以提这个是想说,如果遇到定义的同时赋值,你一个清楚是在给谁赋值(定义部分的变量名)
这句话什么意思呢?
-
比如
int *p = &a;
,定义部分的变量是p,那你赋值就要给一个地址(指针),因为p是指针,所以必须用地址给p赋值 -
再复杂点的
int p[4] =
如果让你定义的同时并赋值,那你应该赋什么值。既然变量名p是个数组,你就应该赋值一个数组,即int p[4] = {1, 2, 3, 4};
int *p;
int *&r = p;
r是个引用,相对于给指向int型的指针起别名。右边是赋值,所以必须用指针给r“赋值”