二维数组指针

摘要:
当我最近回顾C语言时,我使用了二维数组的指针作为函数参数。例如,int(*a)[2],void fun(inta[][2]){a[1][1]=3;}void fun1(int(*a)[2]){a[1][1]=3;int**p=(int**)a,则p[0]=0;我们都知道char*a=“abcd”和char a[]=“abcd”,int*p=b;int*p2=b[0];

     最近复习C语言的时候用到了2维数组的指针做为函数形参传入,网上查了一些方法,觉得颇有深度,做了一番研究,感受颇深,写下来算是做为第一天来博客园的见证。  

  首先网上查了一下,就是传入2维数组指针来作为对参数的形参,如 int (*a)[2],int a[2][2] 等形式;

void fun(int a[][2])
{
	a[1][1]=3;
}
void fun1(int (*a)[2])
{
	a[1][1]=3;
}	


int main()
{
	int a[2][2]={{1,1},{1,1}};
	fun(a);
	fun1(a);
	
	printf("a[1][1] is %d
",a[1][1]);
}

  结果a[1][1] is 3,由此可以发现以上两种形式可以传入的。由此引发了我更深层次的思考.我开始思考这个2维数组的指针到底是以什么样的形式存在的。

  我当时第一印象就是就是指针的指针,简称2级指针,这方面我查阅了一些资料,如果说2维数组指针是一个2级指针,附代码:

  

int a[2][2]={0,1,2,3}; int **p=(int**)a 则此时p[0]=0;p[1]=1;p[2]=2;p[3]=3; 而p[0][0]=*(*(p+0)+0)=**p; p[0][1]=*(*(p+0)+1); 对于p[0][0]:由于*p=0; ====> **p=*(0);引用地址为零的内存,必然是错误的。 对于p[0][1]=*(*p+1)====>*(4),引用了非法内存同样

  我们知道,对于任何一个指针类型的变量,他会把他内存上连续的四个字节的内容当做地址,如果数组指针是2级指针,那么它的第一次取值就是可能指向非法地址。

 另外指针是可以作为左值的,他是一个变量可以被赋值,至少就我们现在看到的2维数组指针都是固定不变的,由以上两点就可以说明其问题。

 此外数组和指针的关系我就不加以评论。我们都知道char *a="abcd"与char a[]="abcd",是不同的,一个是在编译中确定,一个是在运行时赋值,在以后的存取中,在栈上的数组指针所指向的字符串(例如堆)快。这是题外话,我就不加以赘述了,我只是想说明光是在一维数组我们就该明确的知道指针和数组的不同,由此推断出2维数组的不同也是一种佐证。

 我们已经知道了数组指针和2维指针的区别,那么数组指针到底是怎么样的呢?或者我们明确一点目的,即我们到底如何用指针处理数组。

  说道2维数组,我相信任何一本C语言的书上都说明了一个明确的事实,那就是数组在存储上地址都是连续的,2维数组在存储的时候是按照先行后列的顺序依次存储的,当把每一行看作一个整体,即视为一个大的数组元素时,这个存储的二维数组也就变成了一个一维数组了。而每个大数组元素对应二维数组的一行,我们就称之为行数组元素,显然每个行数组元素都是一个一维数组。

  搞清了这个概念,我们先用一种简单的办法来,贴代码

int b[3][3]={1,2,3,4,5,6,7,8,9};
int *p=b;
int *p2=b[0];
printf("p[8] is %d
",p[8]);
printf("p2[8] is %d
",p2[8]);
printf(" *(p+2*3+2) is %d
",*(p+2*3+2));
printf(" *(p2+2*3+2) is %d
",*(p2+2*3+2));

 以上的代码编译的时候会报错,这是因为warning: initialization from incompatible pointer type,这个问题我等下会说,但是我们可以看到我定义了一个int *的指针指向数组的首地址,从而根据数组在内存上的连续排序取值。

 相信看过上述代码的人会纠结下b与b【0】在代码中的意义,我现在先来解释下这两者的区别,从而引发我下一种方法来用指针表示2维数组。

  对上述二维数组b,虽然b[0]、b都是数组首地址,但二者指向的对象不同,b[0]是一维数组的名字,它指向的是b[0]数组的首元素,对其进行“*”运算,得到的是一个数组元素值,即b[0]数组首元素值,因此,*b0]与b[0][0]是同一个值;而b是一个二维数组的名字,它指向的是它所属元素的首元素,它的每一个元素都是一个行数组,因此,它的指针移动单位是“行”,所以b+i指向的是第i个行数组,即指向b[i]。对b进行“*”运算,得到的是一维数组a[0]的首地址,即*a与a[0]是同一个值。当用int *p;定义指针p时,p的指向是一个int型数据,而不是一个地址,因此,用a[0]对p赋值是正确的,而用a对p赋值是错误的。这一点请读者务必注意。尽管这是错误的,但是编译器依旧会优化这个错误,报个警告出来。

  由上述说明,我们还可以得到二维数组元素的一种表示方法:
对于二维数组b,其b[0]数组由b指向,b[1]数组则由b+1指向,b[2]数组由b+2指向,以此类推。因此,*b与b[0]等价、b(a+1)与b[1]等价、*(b+2)与b[2]等价,┅,即对于b[i]数组,由*(b+i)指向。由此,对于数组元素b[i][j],用数组名b的表示形式为:
*(*(b+i)+j)
指向该元素的指针为:
*(b+i)+j

 是不是感觉有点迷糊,那么我换一种写法来表示

b=&b[0] b[0].....b[i]   类型 int(*p)[M]
b[0]=&b[0][0] b[0][0].......b[0][i]  类型int

 贴代码验证:

printf("b  address is %p
",b);
printf("&b[0]  address is %p
",&b[0]);
printf("b[0]  address is %p
",b[0]);
printf("&b[0][0] adddress is %p
",&b[0][0]);
printf("b+1  address is %p
",b+1);
printf("b+1  address is %p
",b[0]+1);

b  address is 0xbfb71e48
&b[0]  address is 0xbfb71e48
b[0]  address is 0xbfb71e48
&b[0][0] adddress is 0xbfb71e48
b+1  address is 0xbfb71e54
b+1  address is 0xbfb71e4c       一目了然,不用我多说了吧。

既然我们搞定这种形式,那么我们如何用指针来实现它呢, 首先我知道了a的数组元素是int(*p)[N]的类型,那么我定义一个这种类型的指向它,这时候我们发现编译器的那个waring不报错了,说明我的猜测是对的。

老规矩,贴代码验证:

int (*p1)[3]=b;
printf("p1[0][1] is %d
",p1[0][1]);
printf("*(*(p1+0)+1) is %d
",*(*(p1+0)+1));
printf(" (*p1)[1] is %d
",(*p1)[1]);
printf(" (*p1)[8] is %d
",(*p1)[8]);

p1[0][1] is 2
*(*(p1+0)+1) is 2
 (*p1)[1] is 2
 (*p1)[8] is 9      结果证明我们用int (*p)[3] 类型的指针来实现b+i 与b[i]+j的指针移动并且取值是可以实现的。

注: (*p1)[8] is 9  我想

int *p=b;
int *p2=b[0];
printf("p[8] is %d ",p[8]);
printf("p2[8] is %d ",p2[8]);  这段代码已经解释清楚了,但是我建议最好不要这样做。

好了就说这么多,参考了网上很多大牛的解释,有错误希望指正。

 

    

免责声明:文章转载自《二维数组指针》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Win7系统中如何查看当前文件被哪一个程序占用了远程桌面连接使用空密码的administrator账号下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

深度解析C语言中的sizeof

1)解析C语言中的sizeof 一、sizeof的概念  sizeof是C语言的一种单目操作符,如C语言的其他操作符++、--等。它并不是函数。sizeof操作符以字节形式给出了其操作数的存储大小。操作数可以是一个表达式或括在括号内的类型名。操作数的存储大小由操作数的类型决定。 二、sizeof的使用方法  1、用于数据类型   sizeof使用形式:...

redis list结构

一个功能肯定有其应用场景: PUSH和POP操作,其实是队列的基本操作。Redis的list就是一个极其强大的队列系统。我们在哪些地方会用到队列呢?下面,我们说两个例子: a,评论系统 逛过微博的筒子们应该都对评论系统有了解。我们在看完一条微博之后,常常会评论一番,或者看看其他人的吐槽。每条评论的记录都是按照时间顺序排序的。我们读的时候也是这个顺序。这时,...

澄清VB调用API时字符串参数的困惑

声明:本文部分内容来源于网络! 首先,我认为这样花费精力去研究VB调用API字符串的种种猫腻是很有必要的。本着严谨和发现问题与解决问题的原则,和为了能更好的认识问题的本质,我特写了这篇冗长的讨论。网上有很多关于此的讨论,但比较杂乱,目的不明确,也很难懂。在此也就是做个总结吧!让大家能有一个清楚认识。 我的问题是从调用内存API时参数的ByVal VarPt...

jconsole使用

先看一张图 根据JConsole和任务管理器对比,堆内存大小在250M左右,差不多空跑一个程序用idea启动springboot就是这个大小 项目启动初始类在一万个左右,活动线程50个上下,cpu利用率可以忽略。 idea工具本身占用内存在1.7G,这个貌似有点大,具体的内存使用需要后续学习? google浏览器1G内存感觉也是逆天了。 内存分析这张图比...

Hive调优实战[转]

Hive优化总结 【转自:http://sznmail.iteye.com/blog/1499789】 优化时,把hive sql当做map reduce程序来读,会有意想不到的惊喜。 理解hadoop的核心能力,是hive优化的根本。这是这一年来,项目组所有成员宝贵的经验总结。   长期观察hadoop处理数据的过程,有几个显著的特征: 1.不怕数据多...

jQuery Easing 动画效果扩展--使用Easing插件,让你的动画更具美感。

jQuery  Easing 是一款比较老的jQuery插件,在很多网站都有应用,尤其是在一些页面滚动、幻灯片切换等场景应用比较多。它非常小巧,且有多种动画方案供选择,使用简单,而且免费。 引入Easing js文件 该插件基于jQuery,所以需要同时引入jQuery库文件和Easing js文件。 <script type="text/javas...