大家好,我是 同学小张,持续学习C++进阶知识和AI大模型应用实战案例,持续分享,欢迎大家点赞+关注,共同学习和进步。
重学C++系列文章,在会用的基础上深入探讨底层原理和实现,适合有一定C++基础,想在C++方向上持续学习和进阶的同学。争取让你每天用5-10分钟,了解一些以前没有注意到的细节。
是否你也和我一样,到现在也分不清指针数组和数组指针?这篇文档带你重新认识它们,彻底认清它们。
1. 什么是指针数组和数组指针?
1.1 字面意思理解
看到一个很简单的从字面意思理解 指针数组 和 数组指针 的方式 —> 在中间加个“的”:
- 指针数组:指针的数组,本质上是个数组,数组里面的元素是指针
- 数组指针:数组的指针,本质上是个指针,是一个指向数组的指针
是不是瞬间觉得很好区分了?
但是光从概念上区分是没用的,要从表达式区分出它们才行。
1.2 表达式判断是指针数组还是数组指针
int *ptr1[10]; int (*ptr2)[10];
如上面两种声明方式,你能区分出哪种是数组指针,哪种是指针数组吗?
区分方式如下:
(1)首先得了解运算符的优先级,多了不说,只需要知道 () > []
即可。
(2)知道了运算符优先级,那我们来看:
int *ptr1[10]
中,优先级高的是[10]
,说明这整个表达式ptr1
代表的是个数组,其它的都是修饰这个数组的,所以这是个指针的数组,指针数组。
int (*ptr2)[10]
中,优先级高的是(*ptr2)
,说明整个表达式代表的是个指针,其它的都是修饰这个指针的,所以这是个数组的指针,数组指针。
2. 指针数组
2.1 原理图
指针数组,指针的数组,数组里面都是指针:
使用时将里面的每一个元素都当作普通指针去使用就可以了。
2.2 测试代码
char *str[3] = {"lirendada","C语言","C Language"}; std::printf("str+1的值:%s\n", *(str+1)); std::printf("str+1的值:%s\n", str[1]); std::printf("str+1的地址:%p\n", str+1); std::printf("str+1的地址:%p\n", &str[1]); std::printf("str+1指向的地址:%p\n", str[1]); std::printf("str+1指向的地址:%p\n", *(str+1));
运行结果:
2.3 运行结果详解
(1)*(str+1) 与 str[1] 等价,都是取第1个字符串的值
(2)整体的结构可以用下图表示:数组的每个元素都是个char*指针,指向一个字符串,字符串存储在全局区,数组的元素存放在栈区。
(3)每个char*指针指向的是字符串的首地址。
2.4 补充细节
参考:https://blog.csdn.net/lirendada/article/details/122931987
(1)字符串指针数组赋值时,每个元素必须是char*类型,也就是必须也是指针类型或能退化成指针的数组类型才可以,否则报错:
(2)二维数组与指针数组的区别
char *p1[]={"lirendada","C","C++"}; char p2[][8]={"liren","C","C++"};
*p1
,*(p1+1)
,*(p1+2)
:所指向的字符串常量是不规则长度的,且sizeof(p1)=12
。p2[0]
,p2[1]
,p2[2]
所指向的字符串都是一定长度的,且sizeof(p2)=24
。
3. 数组指针
3.1 原理图
数组指针,数组的指针,指向数组的一个指针:
数组指针ptr2指向一个数组的首地址。下面以一段测试代码来看下数组指针怎么使用。
3.2 测试代码
int a[5] = {0,1,2,3,4}; int (*ptr2)[5] = &a; std::printf("a的地址:%p\n", &a); std::printf("ptr2指向的地址:%p\n", ptr2); std::printf("ptr2自身的地址:%p\n", &ptr2); std::printf("a[1]的值: %d\n", a[1]); std::printf("使用ptr2访问a[1]的值:%d\n", (*ptr2)[1]); std::printf("使用ptr2访问a[1]的值:%d\n", *((*ptr2)+1));
运行结果:
3.3 运行结果详解
(1)首先看数组指针的赋值语句:第二句 &a
,必须带"&
"号
int a[5] = {0,1,2,3,4}; int (*ptr2)[5] = &a;
(2)a的地址和ptr2指向的地址相同,这就是数组指针的本质(也和我们上面的原理图一致),指向一个数组的首地址。
(3)ptr2自身有个地址,如果再仔细一点看,它的地址与数组首地址差了8个字节,一个地址的距离,也就是说,数组a和ptr2在栈内存空间中是挨着的。
(4)最后三行代码提供了三种访问a[1]元素的方式,这三种方式等价。读者可以先思考下为什么这三种方式是等价的。后面一起解答。
3.4 升级难度:二维数组的数组指针
以二维数组:int b[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
为例
3.4.1 二维数组
(1)内存形式
二维数组虽然我们认为有行有列,但实际在内存中,是一块连续的内存区域,并没有行和列的概念。
(2)一些测试
std::printf("数组名b的地址:%p\n", b); std::printf("b[0]的地址:%p\n", &(b[0])); std::printf("b+1的地址:%p\n", b+1); std::printf("b[1]的地址:%p\n", &(b[1])); std::printf("b+1的大小:%llu\n", sizeof(*(b+1))); std::printf("第一行第二列 b[1][2]值的访问:%d", *(*(b+1)+2)); // 输出: // 数组名b的地址:00000000005ffe00 // b[0]的地址:00000000005ffe00 // b+1的地址:00000000005ffe10 // b[1]的地址:00000000005ffe10 // b+1的大小:16 // 第一行第二列 b[1][2]值的访问:7
- 数组名b代表的是整个数组的首地址,也是第0行的首地址。
b+1
代表的是第1行的首地址*(b+1)
的大小为 16,4个int值,也就是代表第一行的所有数据*(b+1)+2
,当*(b+1)
作为表达式中的一项时,会作为这一行的首地址使用,所以*(b+1)
表示第1行的首地址,再+2
表示这一行的第2列。
3.4.2 二维数组指针
定义方法:
int (*ptr3)[4] = b;
注意在二维数组时,b前面没有了
&
符号,因为b本身就代表一个int[4]
的数组了。
从直观上理解,*ptr3是不是就相当于代替了原来的 b[0], b[1] 和 b[2] ?所以,ptr3应该与b[0]指向相同的地址,ptr3+1与b[1]指向相同的地址。写如下代码测试上面的结论:
std::printf("b[0]的地址:%p\n", &(b[0])); std::printf("ptr3指向的地址:%p\n", ptr3); std::printf("b[1]的地址:%p\n", &(b[1])); std::printf("ptr3+1指向的地址:%p\n", ptr3+1); std::printf("第一行第二列 b[1][2]值的访问:%d\n", *(*(ptr3+1)+2)); // 输出结果 // b[0]的地址:00000000005ffe00 // ptr3指向的地址:00000000005ffe00 // b[1]的地址:00000000005ffe10 // ptr3+1指向的地址:00000000005ffe10 // 第一行第二列 b[1][2]值的访问:7
3.5 数组名与数组指针的等价关系
通过上面的测试代码不难发现,数组名与数组指针之间具有以下等价关系:
b+i == ptr3+i b[i] == ptr3[i] == *(b+i) == *(ptr3+i) b[i][j] == ptr3[i][j] == *(b[i]+j) == *(ptr3[i]+j) == *(*(b+i)+j) == *(*(ptr3+i)+j)
4. 总结
总结一下数组指针与指针数组的区别:
(1)数组指针是指向数组的指针,本质是一个指针;指针数组是元素全都是指针的数组,本质是一个数组。
(2)基于两者的本质区别,数组指针大小就是4字节(32位平台)或8字节(64位平台),而指针数组的大小不止取决于平台的位数,还取决于数组的大小。
(3)最后再上一个区别的图,都以一个二维数组为例:数组指针指向这个二维数组首行首元素的地址。指针数组首先是包含3个指针,每个指针指向一行的首元素地址。
5. 补充问题
a
为一维数组,为什么下面的打印a
与&a
地址是相同的?欢迎讨论。
std::printf("a的地址:%p\n", a); // 输出:00000000005ffe40 std::printf("a的地址:%p\n", &a); // 输出:00000000005ffe40
提醒一句:一定要动手去实践一下!没有任何一篇文章看了之后就能彻底搞懂指针,必须亲身体验才能加深印象!
如果觉得本文对你有帮助,麻烦点个赞和关注呗 ~~~
- 大家好,我是 同学小张,持续学习C++进阶知识和AI大模型应用实战案例
- 欢迎 点赞 + 关注 👏,持续学习,持续干货输出。
- +v: jasper_8017 一起交流💬,一起进步💪。
- 微信公众号也可搜【同学小张】 🙏
本站文章一览: