说明:int (*p)[4] 和 int *p[4](数组指针和指针数组),如果你是一个初学者,也许当你看到这两个名词的时候,已经懵了。其实,只要你理解了其中的含义.这两个名词对你来说会相当简单并且很有趣,下面,我们就来深入探讨一下究竟什么是数组指针,什么是指针数组。

一.指针数组

1.前面我们已经学过数组了,比如说要创建一个一维整型数组,该怎么创建呢?应该是这样的:int arr[N];其中,arr是数组名,即变量名,N是你所创建的这个数组中的元素个数,而前面的int则是这些元素的类型。所以其实可以将它读作整型变量数组。那万一你所创建的数组元素不是整型和浮点型这些基本类型,而是一个指针类型呢?这就是指针数组了。

2.指针数组,首先它也是一个数组,只不过这个数组中的元素的类型为指针类型,举个例子:double *arr[4],这是一个指针数组,包含四个元素,其中每个元素都是double*类型的,简单来说,它就是一个用来存储指针的数组。用一个图来说明这个指针数组的内存布局:

3.既然指针数组是一个数组,那么它就应该有数组所应具有的一些特点。举个例子,对于double* p[4],p+1加的是数组的步长,即一个double*的大小,四个字节(注意:在32位机中,所有指针的大小都为4个字节)。而如果对数组名p进行取地址后,则&p+1加的是sizeof(p),即4*4 = 16个字节,即&p+1就跨过了整个数组。

示例:

 #include<stdio.h>
int main()
{
double *p[4] = {NULL};
printf("p = %p\t",p);
printf("p+1 = %p\n",p+1);
printf("&p = %p\t",&p);
printf("&p+1 = %p\n",&p+1);
return 0;
}

程序运行结果:

二.数组指针

1.指针相信大家都比较熟悉了,比如:int *p定义了一个指针p,该指针指向一个整型数据单元,如果对该指针执行加1操作,则加的是4个字节;又如char *q定义了一个指针q,该指针指向一个字符型的数据单元,如果对该指针执行加1操作,则实际上加的是1(个字节)。那么问题来了,万一要定义一个指针,它所指向的数据单元为一个一维数组怎么办呢?对他执行加1操作又能得到什么呢?这就是数组指针了。

2.数组指针,首先得明白它是一个指针,只不过这个指针指向的数据单元为一个数组,举个例子,现在有一个一维数组int arr[4];现在要定义一个数组指针来指向它,按照一般指针的理解,应该是这样的,int[4]* p;表示定义一个指针p,而该指针的类型为int[4]*型的,但这在编译器中是会报错的,没什么理由,语法规定。实际上对这个数组指针的定义应该是这样的:int (*p)[4] = arr;说实话,这样看着,笔者觉得挺别扭的,不过没办法,编译器就只认这个写法,不过这完全不影响我们按照第一种写法去理解数组指针的本质。

3.上面已经说了,数组指针实质就是一个指针,只不过其指向的类型与基本类型不同罢了。对于基本类型的指针,执行加1加的是指针指向数据类型的字节数,那么对于数组指针呢?显然加1加的也是指针指向数据类型的字节数,那么数组指针指向数据类型的大小怎么判断呢?举个例子:int(*p)[4],下面将通过一张内存数据图对此进行阐述:

如图:该指针里面存的是一个数组的首地址,只不过该指针的类型为int[4] *型,这就导致了该指针的步长为4*4 = 16个字节,所以对该指针执行加一操作,实际上加的是16个字节,即整个数组的大小。

 #include<stdio.h>
int main()
{
int arr[4];
int (*p)[4] = (int(*)[4])arr;
printf("%p\t",p);
printf("%p\n",p+1);
return 0;
}

程序运行结果:

4.数组指针与二维数组的关系是什么呢?首先要知道,二维数组 int arr[m][n] 可以想象成是具有m行,n列的一个数组矩阵,也可以想象成是有m个一维数组,其中每个一维数组里面又有n个int型的元素.那么是否可以用一个类型为int[n] *型的指针指向该二维数组来实现行间跳转访问呢?答案是肯定的!就拿上面例子来说,假如有一个二维数组int arr[m][n],则可以定义一个数组指针为:int (*p)[n] = arr(这里最好强转一下),然后用p对数组进行访问,由以上可讲可知,这里的p+1加的是n*4个字节,即加的是二维数组每行的字节数。

示例:

 #include<stdio.h>
int main()
{
int arr[3][4];
int (*p)[4] = (int(*)[4])arr;
printf("%p\t",p);
printf("%p\n",p+1);
return 0;
}

程序运行结果:

注意:二维数组的存储在内存中实际上是线性存储的,可以说任何数据在内存上的存储都是线性存储的,但这并不影响我们用二维的思维去理解它。

三.下面是一个数组指针当做二维数组名访问数组的示例,只是为了巩固与拓展一下以上,对于二维数组名的具体使用方式,在下次更新(后天)会详细介绍。这里就简单介绍一下,当把二维数组名赋给一个指针数组后,例如如下示例,则该指针就拥有了二维数组名访问二维数组的方式,比如在这里,p代表数组的首地址,由于其拥有了二维数组名的特性,则**p就是二维数组里的第一个元素,而*(*(p+i)+j)是二维数组第i行第j列的元素。

示例:

 #include<stdio.h>
int main()
{
int arr[3][4];
int count = 0;
for(int i = 0;i<3;i++)
for(int j = 0;j<4;j++)
arr[i][j] = count++;
for(int i = 0;i<3;i++)
{
for(int j = 0;j<4;j++)
printf("%2d ",arr[i][j]);
putchar(10);
}
int (*p)[4] = (int(*)[4])arr;
for(int i = 0;i<3;i++)
{
for(int j = 0;j<4;j++)
printf("%2d ",*(*(p+i)+j));
putchar(10);
}
return 0;
}

程序运行结果:

最新文章

  1. SDOI 2016 数字配对
  2. LayaAir引擎——(十)
  3. c语言编写的日历
  4. L - Abbott&#39;s Revenge(比较复杂的bfs)
  5. UIView中触摸事件touchBegin
  6. js基础知识之_流程控制语句
  7. 离线安装.NET 3.5小记
  8. c#实现Javascript的encodeURIComponent()函数
  9. Android登陆界面实现-支持输入框清楚和震动效果功能
  10. 《大型网站系统与JAVA中间件实践学习笔记》-1
  11. 原生JS封装animate运动框架
  12. UNIX环境高级编程——进程关系
  13. Ubuntu安装Hadoop
  14. Leetcode 143. Reorder List(Medium)
  15. 准备开发一个运行在Android上的JavaME模拟器
  16. ObservableCollection&lt;T&gt; 类
  17. JavaScript总结(二)
  18. Floyd_Warshall(任意两点之间的最短路)
  19. 快速排序以及第k小元素的线性选择算法
  20. mysql忘记root密码的解决方法

热门文章

  1. 通达OA 小飞鱼老师OA工作流设计课程教学网络公开课之HTML基础(一)
  2. mysql 常用查询语句记录
  3. bzoj2982 combination——卢卡斯定理
  4. (function(){})();和(function(){}())每个括号的用途和区别
  5. Laravel (5.5.33) 加载过程---make方法(四)
  6. linq 分组
  7. react杂记
  8. Mac下CUDA开启及Tensorflow-gpu 1.4 安装
  9. 实现X*N
  10. JDBC链接数据库步骤