博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C语言基础---14.指针数组 & 数组指针---图解篇
阅读量:2394 次
发布时间:2019-05-10

本文共 5699 字,大约阅读时间需要 18 分钟。

文章目录

0.指针数组 & 数组指针(相见恨晚篇)

《C语言深度剖析》上面的解析如下:

指针数组: 首先它是一个数组,数组的元素都是指针,数组占多少个字节由数组本身的大小决定,每一个元素都是一个指针,在32 位系统下任何类型的指针永远是占4 个字节。它是“储存指针的数组”的简称。

数组指针: 首先它是一个指针,它指向一个数组。在32 位系统下任何类型的指针永远是占4 个字节,至于它指向的数组占多少字节,不知道,具体要看数组大小。它是“指向数组的指针”的简称。

下面到底哪个是数组指针,哪个是指针数组呢:

A) int *p1[10];
B) int (*p2)[10];

  • “[]”的优先级比“*”要高。p1 先与“[]”结合,构成一个数组的定义,数组名为p1,int *修饰的是数组的内容,即数组的每个元素。那现在我们清楚,这是一个数组,其包含10 个指向int 类型数据的指针,即指针数组。
  • 至于p2 就更好理解了,在这里“()”的优先级比“[]”高,“*”号和p2 构成一个指针的定义,指针变量名为p2,int 修饰的是数组的内容,即数组的每个元素。数组在这里并没有名字,是个匿名数组。那现在我们清楚p2 是一个指针,它指向一个包含10 个int 类型数据的数组,即数组指针。

我们可以借助下面的图加深理解:

在这里插入图片描述

这里有个有意思的话题值得探讨一下:

平时我们定义指针都是在数据类型后面加上指针变量名,例如int * p 这种方式。这个指针p2 的定义怎么不是按照这个语法来定义的呢?也许我们应该这样来定义p2:
int ( * ) [10] p2;

真相是:

  • int (*) [10] 才是指针类型,这个不仅代表指针的类型是int,也代表这个指针的偏移位置是4x10 = 40个字节。而普通的指针int * p中,默认移动的位移是4个字节。
  • p2 是指针变量, 所以p2+1 在首地址移动了40个字节
  • 这样看起来的确不错,不过就是样子有些别扭。其实数组指针的原型确实就是这样子的,只不过为了方便与好看把指针变量p2 前移了而已。你私下完全可以这么理解这点

聪明的你,看完上面的讲解好像都理解了,是吧?

如上的解释,我再把重点啰嗦几句:

  • 指针数组:本质是数组,只是里面存放的是多个指针(int * p1[10])。此处的p1是数组名,指针名没有。如果想获取指针&p1[i],这样可以获取指针
  • 数组指针:本质是指针,虽然初始化的时候指向了一个数组( int(p2)[10] ),但其实只是一个指针。p2是指针变量,想通过指针获取地址,可以用指针的偏移去获取,例如p2 + 1,代表移动一个数据类型的int() [10]的,40个字节的位置
  • 普通指针,int * p ,数据类型是int ,所以每次p + 1移动的是4个字节
  • 有关指针的偏移,可以参考博文:https://blog.csdn.net/chenmozhe22/article/details/106410963(图文并茂)

1.指针数组

#include 
int main() {
int i; char* pch[5] = {
"我", "爱", "你", "祖", "国" }; // 此处初始化只能是5个元素,多一个少一个都不行 printf("%s \n", pch[0]); // pch是数组名,所以通过索引直接可以获取到元素内的每个值 printf("%s \n", *(&pch[0])); // 既然每个变量可以找到,就能通过* + & + 变量方式,对指针取值 *(&pch[3]) = "中"; //pch[3] = "中";方式也一样效果 for (i = 0; i < 5; i++) {
printf("%s, ", pch[i]); }; printf("\n"); printf("%s\n", *(&pch[1] + 1)); // 通过指针获取其他元素 printf("%d \n", sizeof(pch)); // 因为pch是指针数组,里面存放的是5个指针,所以4*5 = 20个字节 return 0;};
我我我, 爱, 你, 中, 国,你20

2.数组指针

1.二维数组 &普通指针

#include 
int main() {
int a[3][4] = {
{
0, 1, 2, 3 }, {
4, 5, 6, 7 }, {
8, 9, 10, 11 } }; int* p; p = a[0]; printf("%p \n", p); printf("%d \n", *p); printf("%d \n", *(p+1)); printf("=================\n"); printf("%p \n", a); printf("%p \n", &a[0]); printf("%p \n", a[0]); printf("%p \n", *a); printf("%d \n", **a); return 0;}
00A7F6B801=================00A7F6B800A7F6B800A7F6B800A7F6B80

在这里插入图片描述

通过如上分析:

  • p = a[0];相当于是将普通的指针,指向了二维数组的首行。
  • 虽然无法获取每一行的内容,但可通过一个一个的移动,逐步获取其他行的数据
  • a / a[0] / &a[0] 打印的结果都是一样的

2.二维数组 & 数组指针----表示行指针的概念

#include 
int main() {
int list[2][5] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int* p1 = &list; // 普通指针指向二维数组 printf("%p \n", p1); printf("%p \n", p1 + 1); // &list[0] + 1,每次移动一个int类型的字节数 printf("%p \n", p1 + 2); // &list[0] + 2 printf("===================\n"); int(*p2)[5] = &list; // 数组指针:指向了某个数组的指针 printf("%p \n", p2); //p2 和 *p2 的地址是一样的 printf("%p \n", *p2+1); //*p2 + 1代表指针的首地址,移动一个int类型字节数 printf("%p \n", *p2+2); printf("===================\n"); printf("%p \n", p2); printf("%p \n", *(p2 + 1)); // p2指针移动一个int[5]类型字节数,4x5 = 20字节数 printf("%p \n", *(p2 + 2)); return 0;};
00F3FCDC			00F3FCE0			00F3FCE4===================00F3FCDC00F3FCE000F3FCE4===================00F3FCDC00F3FCF0		// 这个地址,相比00F3FCDC,大了20个字节大小00F3FD04

通过如上我们发现,普通指针只能通过一个元素一个元素的移动来取值,最终的取值形状如下

在这里插入图片描述

数组指针,因为指针的类型是int(*)[5],所以每次可以通过 *(p+1)移动20个字节大小空间

在这里插入图片描述

3.通过数组指针获取某行某列的值

根据数组指针和普通指针偏移的特定,我们组合一下,就可以很方便的获取某行某列的最终的值了!

在这里插入图片描述

#include 
int main() {
int list[2][5] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int(*p2)[5] = &list; // 数组指针:指向了某个数组的指针,方式二 printf("%p \n", p2); // 打印首行地址---首行的首地址 printf("%p \n", *(p2 + 1)); // 打印第二行地址---第二行首地址 printf("===================\n"); printf("%p \n", *(p2 + 1)+1); // 第二行第二个元素地址 printf("%p \n", *(p2 + 1)+2); // 第二行第三个元素地址 return 0;};
008FFC58		// 首行首地址008FFC6C		// 二行首地址===================008FFC70		//二行第二个地址008FFC74		//二行第三个地址

3.再谈二维数组

有关二维数组的概念和基础,可参考:https://blog.csdn.net/chenmozhe22/article/details/106280578

1.数组指针 & 数组名

#include 
int main() {
int a[3][4] = {
{
0, 1, 2, 3 }, {
4, 5, 6, 7 },{
8 , 9, 10, 11 }}; int(*p)[4]; // 数组指针:p int i, j; p = a; printf("%p \n", p); printf("%p \n", *p); printf("%d \n", **p); printf("=================\n"); printf("%p \n", a); printf("%p \n", *a); printf("%d \n", **a); return 0;}
00D3FD9400D3FD940=================00D3FD9400D3FD940

在这里插入图片描述

根据代码,发现:

  • **p / **a才能得到,二维数组元素对应的值
    -在一维数组中(*p可以得到最终的元素),p代表的是指针变量;那么二维数组(**p可以得到最终的元素),那 *p 就可以看做指针变量。
  • p则是真实指向二维数组,首元素的指针,这里有个概念,p不是指针的指针,可参考博文:C—数组名与指针的恩怨情仇
  • 我们可以通过指针、地址两种方式,分别获取并修改二维数组中的值。

2.获取二维数组中元素的值—指针循环

有关指针的偏移,可以参考:

https://blog.csdn.net/chenmozhe22/article/details/106410963

如上代码,经过优化之后如下:

#include 
int main() {
int a[3][4] = {
{
0, 1, 2, 3 }, {
4, 5, 6, 7 },{
8 , 9, 10, 11 }}; int(*p)[4]; int i, j; p = a; for (i = 0; i < 3; i++) {
for (j = 0; j < 4; j++) printf("%2d ", *(*(p + i) + j)); printf("\n"); } return 0;}
0   1   2   3 4   5   6   7 8   9  10  11

3.获取二维数组中元素的值–地址循环

int main() {
char list[2][5] = {
{
'1', '2', '3', '4', '5' }, {
'a', 'b', 'c', 'd', 'e' }}; printf("%c \n", list[0][0]); //获取第一行的第一个元素 printf("%c \n", list[1][0]); //获取第二行的第一个元素 printf("%p \n", &list[0]); printf("%p \n", &list[1]); printf("%d-%d",&list[1],&list[0]); // 计算第一行第一个元素和第二行第一个元素相差的字节数 return 0; };
1a00DBFD3C00DBFD4114417217-14417212
#include 
int main() {
char list[2][5] = {
{
'1', '2', '3', '4', '5' }, {
'6', '7', '8', '9', '0' } }; for (int i = 0; i < 2; i++) {
for (int j = 0; j < 5; j++) printf("%c \t", *(&list[i][j])); printf("\n"); }; return 0;};
1       2       3       4       56       7       8       9       0

文章参考:

  • 《C语言深度剖析》(强烈推荐大家看看,特别是初学者,昨天买了一本,家中常备!)
  • https://www.cnblogs.com/hongcha717/archive/2010/10/24/1859780.html
  • https://www.cnblogs.com/mq0036/p/3382732.html#commentform
你可能感兴趣的文章
Java Web 三层架构详解
查看>>
iphone for PPT遥控器 MyPoint PowerPoint Remote
查看>>
ZPanel 10.0.0.2 Remote Command Execution
查看>>
Using Mimikatz Alpha or Getting Clear Text Passwords with a Microsoft Tool
查看>>
宁皓网bootstrap
查看>>
Hive作业优化总结(来自一号店)
查看>>
九.redis学习笔记之虚拟内存
查看>>
java ireport jasperreport 配套使用(三) 使用Parameters
查看>>
关于二维数组和指向指针的指针
查看>>
http get post head
查看>>
关于C语言中函数调用和参数传递机制的探讨
查看>>
堆栈溢出从入门到提高
查看>>
Linux环境进程间通信 共享内存(下)
查看>>
Linux环境进程间通信 共享内存(上)
查看>>
Linux select函数(二)
查看>>
Yacc使用
查看>>
C语言里面的嵌套声明的读法
查看>>
kmalloc详解
查看>>
linux下gdb单步调试(中)
查看>>
写一个块设备驱动-第9章
查看>>