二维数组的认识及其表示元素的两种方式

/*
 ============================================================================
 Name        : TeatArr.c
 Author      : lf
 Version     :
 Copyright   : Your copyright notice
 Description : 二维数组的认识以及其表示元素的两种方式
  备注说明
 1 要理解二维数组的存储方式.
 2 实际上利用a[i][j]的方式并不"正统",但这靠近我们的
        常识一些,更本质和应该的还是利用指针和数组名.

   一个二维数组,比如:
 int array[3][4] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
  它在内存中存在方式如下:
 0X00F8FE4C    1    2    3    4
 0X00F8FE5C    5    6    7    8
 0X00F8FE6C    9   10   11   12
   即:
   从0X00F8FE4C地址开始存放了 1 2 3 4
   从0X00F8FE5C地址开始存放了5 6 7 8
   从0X00F8FE6C地址开始存放了9 10 11 12

  参考资料:
 http://blog.csdn.net/iu_81/article/details/1782642
 http://www.jb51.net/article/54220.htm
 Thank you very much
 ============================================================================
 */

#include <stdio.h>
#include <stdlib.h>

int main(void) {
//	test0();
//	test1();
	test2();
//	test3();
	return EXIT_SUCCESS;
}

/**
 * 一维数组的两种表达方式
 * int *p=&a
 * 指针p执行了数组a的首元素.
 * 又因为*p是取内容,所以*(p+N)
 * 取的就是第N个元素的内容
 */
void test0(){
	int a[5]={0,1,2,3,4};
	//第一种方式:下标表示法
	int i=a[2];
	printf("i=%d\n",i);

	//第二种方式:指针表示
	int *p=&a;
	int j=*(p+2);
	printf("j=%d\n",j);
	printf("==========\n");

	//在一维数组中数组名就是首元素的地址
	//所以a和&a[0]是等值的.
	//但是注意&a虽然值和它们两个相等,但是它是
	//代表整个数组的块地址的起始值.
	//这点通过sizeof()可以看出来
	printf("a=%x,sizeof(*a)=%d\n",a,sizeof(*a));
	printf("&a[0]=%x,sizeof(*&a[0])=%d\n",&a[0],sizeof(*&a[0]));
	printf("&a=%x,sizeof(*&a)=%d\n",&a,sizeof(*&a));
	printf("==========\n");
}


/**
 * 将二维数组a[3][4]看作一维数组a[3]
 * 一维数组a[3]中每个元素又是一个一维数组.
 * 那么&a[i]表示某一行的地址块的首地址
 * 所以sizeof(*&a[0])=4*4=16
 * 即sizeof(a[0])=4*4=16
 * &a[i]是个地址,所以sizeof(&a[0])=4或者8字节
 *
 * 在一维数组:
 * int c[3]={0,1,2};
 * 中数组名c就是第一个元素的首地址,所以*c=0即取出了第一个元素.
 * 同样的道理a[i]也是一个一维数组,那么*a[i]就是取出了第i行的
 * 第一个元素.比如*a[0]=0
 */
void test1() {
	int i=9527;
	int *p=&i;
	//p=8d49c90c,&p=8d49c900,*&p=8d49c90c,*p=9527
	//p是p指向的内存单元,&p是p本身的内存单元,
	//*&p取出p中所指向的内存单元就是p(和第一个值一样)
	//*p取出指针指向的内存单元的值
	printf("p=%x,&p=%x,*&p=%x,*p=%d%x\n",p,&p,*&p,*p);



	int c[3]={0,1,2};
	//*c=0,c=8d49c8f0,&c=8d49c8f0,*&c=8d49c8f0
	//*c好理解,取出了数组中的第一个元素
	//c是数组第一个元素的首地址,这个也好理解
	//&c是整个数组的地址块的首地址.
	//*&c是什么意思呢?
	//我们习惯简单地说:由于*和&抵消了,所以*&c就是c.
	//从数值上看也确实是这样.
	//*&c还可以怎么理解呢?
	//注意上面的例子:
	//int i=9527;
	//int *p=&i;
	//*&p的值就是p,即*&p的值和p是一样的.
	//其实此处是非常相似的,可以这么看:
	//int *p=&c;
	//就是说把c的地址保存在了p那个内存单元里面
	//那么*&p和p是一样的均为整个数组c的地址块的首地址
	//所以*&c可以这么理解:
	//第一步:&c取出了c的地址放到了某个内存单元里面
	//第二步:*&c即从某个内存单元里取值,取出的值当然是c的地址
	//这个过程和上面是一样的.只是没有用一个指针变量明确存放c的地址而已
	printf("*c=%d,c=%x,&c=%x,*&c=%x\n",*c,c,&c,*&c);
	printf("===========\n");

	int a[3][4] = { { 0, 1, 2, 3 },
			        { 4, 5, 6, 7 },
			        { 8, 9, 10, 11 } };


	//&a[0]和a[0]的数值相等,但是含义不同.
	//&a[0]是整个行的块地址的起始地址
	//a[0]是这行第一个元素的首地址
	//*a[0]取得了这行的第一个元素的值
	//这里把a[0]看做一个一维数组就很好理解了.
	printf("&a[0]=%x,a[0]=%x,*a[0]=%d\n",&a[0],a[0],*a[0]);

	printf("sizeof(*&a[0])=%d,,sizeof(a[0])=%d,sizeof(&a[0])=%d,sizeof(*a[0])=%d\n",
			sizeof(*&a[0]),sizeof(a[0]),sizeof(&a[0]),sizeof(*a[0]));

	printf("===========\n");

}



/**
 * 二维数组的认识
 *
 * 关于二维数组的指针表示方式,简述如下:
 * 二维数组的每个元素是一个一维数组.
 * 这个是认识和分析二维数组的基础和核心
 *
 * 一个二维数组,比如:
 * int array[3][4] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
 * 它在内存中存在方式如下:
 * 0X00F8FE4C    1    2    3    4
 * 0X00F8FE5C    5    6    7    8
 * 0X00F8FE6C    9   10   11   12
 * 即:
 * 从0X00F8FE4C地址开始存放了 1 2 3 4
 * 从0X00F8FE5C地址开始存放了5 6 7 8
 * 从0X00F8FE6C地址开始存放了9 10 11 12
 * 从本质上而言二维数组的元素在内存中还是线性地按序存放的.
 *
 * 把二维数组a[3][4]可以看成是有3个元素的一维数组a[3]:
 * {0X00F8FE4C,0X00F8FE5C,0X00F8FE6C}
 * 注意的问题:
 * 0 这个一维数组a[3]是抽象的,不是很具体
 * 1 a[3]每个元素都是一个行地址.
 *   a[3]每个元素的值就是每行即每个一维数组的首地址.
 * 2 这个数组的首地址就是0X00F8FE4C!!!!!
 *   同理第2个元素的地址就是0X00F8FE5C,其值也是这个值.
 * 3 二维数组a[3][4]看成是有3个元素的一维数组a[3].
 *   这里的行数为3得到了体现,那么列数4在哪里体现的呢?
 *   这个a[3]的步长为4个整型长度即16字节.
 *   比如int b[3]={0,1,2};
 *   因为它每个元素都是int类型的,所以它的步长就是4个字节
 *   b代表第一个元素的地址,b+1代表了第二个元素的地址;
 *   从b到b+1移动了4个字节.
 *   所以这里的a[0]到a[1]移动了4*4=16个字节
 *
 *
 * 利用下标表示法:
 * 每个元素的地址用下标表示法为&a[i]其值是*&a[i]即a[i]
 *
 * 利用指针表示法(数组名表示法):
 * 每个元素的地址表示为a+i,其值为*(a+i)
 *
 * 所以可以说:
 * &a[i]与a+i等价
 * a[i]与*(a+i)等价
 *
 * 这只是两种不同表达方式的差异罢了,获取到的值当然是一样的.
 * 这两种方式即a[i]和*(a+i)都获取到了a[3]每个元素的值
 * 也是每个一维数组的起始地址.
 *
 * 小结:
 * 获取每个一维数组里第j个元素的地址的两种方式就分别是:
 * a[i]+j和*(a+i)+j
 * 获取每个一维数组里第j个元素的内容的两种方式就分别是:
 * *(a[i]+j)和*(*(a+i)+j)
 * 所以可以说:
 * a[i]+j和*(a+i)+j等价,都是用来取地址的
 * *(a[i]+j)和*(*(a+i)+j)等价,都是用来取内容的
 *
 * 最后,再次提醒a[3]每个元素的地址及其内容是一样的.
 */
void test2(){

	int a[3][4] = { { 0, 1, 2, 3 },
				    { 4, 5, 6, 7 },
				    { 8, 9, 10, 11 } };

	//获取a[3]每个元素地址和其对应的内容的两种方式:
	//&a[i],a[i]和a+i,*(a+i)
	printf("&a[0]=%x,a[0]=%x <==> a+0=%x,*(a+0)=%x\n",&a[0],a[0],a+0,*(a+0));
	printf("&a[1]=%x,a[1]=%x <==> a+1=%x,*(a+1)=%x\n",&a[1],a[1],a+1,*(a+1));
	printf("&a[2]=%x,a[2]=%x <==> a+2=%x,*(a+2)=%x\n",&a[2],a[2],a+2,*(a+2));
	printf("==========\n");

	//访问第二行的第三个元素:
	printf("a[1]+2=%x,*(a[1]+2)=%d\n",a[1]+2,*(a[1]+2));
	printf("*(a+1)+2=%x,*(*(a+1)+2)=%d\n",*(a+1)+2,*(*(a+1)+2));

	printf("==========\n");
}




/**
 * 二维数组的两种表达方式
 *
 * 利用下标方式访问i行j列的数据很简单:a[i][j]
 *
 * 如果用指针的方式又该是怎么样呢?
 * 第一步:找到行
 * 从test2()中我们已经知道了行地址是*(a+i)
 * 第二步:找到列
 * 找到了行再找列就简单多了,偏移j个单位就行
 * 得到地址为*(a+i)+j
 * 所以取值为*(*(a+i)+j);
 *
 */
void test3(){
	int a[3][4] = { { 0, 1, 2, 3 },
			        { 4, 5, 6, 7 },
			        { 8, 9, 10, 11 } };

	//第一种方式:下标表示法
	int i=a[2][3];
	printf("i=%d\n",i);

	//第二种方式:指针表示
	int j=*(*(a+2)+3);
	printf("j=%d\n",j);
}






///**
// * 这是以前我土鳖的认识方法。。。
// * 也就不删了。。。
// *
// *
// * 二维数组的认识
// *
// * 关于二维数组的指针表示方式,简述如下:
// * 二维数组的每个元素是一个一维数组.
// * 比如此处的a[3][4]可以看成是有3个元素的一维
// * 数组,每个元素又是具有4个元素的一维数组.
// * 所以此时可把二维数组a[3][4]看作一维数组a[3]
// * 这个一维数组有三个元素,每个元素又是一个一维数组.
// * 在此打印每行的地址:
// * &a[0]=28feb0
// * &a[1]=28fec0
// * &a[2]=28fed0
// * 可以看到每行的地址相差4*4=16
// * 但是为什么
// * a[0]=28feb0
// * a[1]=28fec0
// * a[2]=28fed0
// * 也打印出来了每行的地址???
// * 这个很简单,在test1()中已经进行了说明:
// * &a是一个地址,再执行*&a还是得到这个地址.
// * 即&a[0]=*&a[0]在数值上也等于a
// * 所以&a[i]与*(&a[i])和a[i]是同一回事!!!
// *
// * 因为a是指向第一行的地址,所以a+1代表第二行的地址,a+2代表第三行的地址
// * 所以&a[i]与a+i是同一回事!!
// *
// * 综上所述:
// * 在二维数组a中
// * &a[i]与*(&a[i])和a[i]还有a+i是等价的
// *
// * 由此可以看到两条演变的路线:
// * &a[i]-->*&a[i]-->a[i]
// * &a[i]-->*&a[i]-->*(&a[i])-->*(a+i)
// *
// */
//void test2(){
//
//
//	int a[3][4] = { { 0, 1, 2, 3 },
//				    { 4, 5, 6, 7 },
//				    { 8, 9, 10, 11 } };
//	//打印二维数组每一行的首地址.&a[i]与a+i等价
//	printf("&a[0]=%x,a+0=%x\n",&a[0],a+0);
//	printf("&a[1]=%x,a+1=%x\n",&a[1],a+1);
//	printf("&a[2]=%x,a+2=%x\n",&a[2],a+2);
//
//
//	printf("&a[0]=%x,a[0]=%x,*(a+0)=%x\n",&a[0],a[0],*(a+0));
//	printf("&a[1]=%x,a[1]=%x,*(a+1)=%x\n",&a[1],a[1],*(a+1));
//	printf("&a[2]=%x,a[2]=%x,*(a+2)=%x\n",&a[2],a[2],*(a+2));
//
//
//	printf("==========\n");
//}


//**
// * 这是以前我土鳖的认识方法。。。
// * 也就不删了。。。
// * 二维数组的认识
// *
// * 关于二维数组的指针表示方式,简述如下:
// * 二维数组的每个元素是一个一维数组.
// * 这个是认识和分析二维数组的基础和核心
// *
// * 一个二维数组,比如:
// * int array[3][4] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
// * 它在内存中存在方式如下:
// * 0X00F8FE4C    1    2    3    4
// * 0X00F8FE5C    5    6    7    8
// * 0X00F8FE6C    9   10   11   12
// * 即:
// * 从0X00F8FE4C地址开始存放了 1 2 3 4
// * 从0X00F8FE5C地址开始存放了5 6 7 8
// * 从0X00F8FE6C地址开始存放了9 10 11 12
// *
// * 把二维数组a[3][4]可以看成是有3个元素的一维数组a[3]
// * 利用下标表示法:每个元素的地址用下标表示法为&a[i]其值是*&a[i]即a[i]
// * 利用指针表示法(数组名表示法):每个元素的地址表示为a+i,其值为*(a+i)
// * 所以可以说:
// * &a[i]与a+i等价
// * a[i]与*(a+i)等价
// * 这只是两种不同表达方式的差异罢了,获取到的值当然是一样的.
// * 这两种方式即a[i]和*(a+i)都获取到了a[3]每个元素的值
// * 也是每个一维数组的起始地址
// *
// * 每个元素的地址用下标表示法为&a[i]其值是*&a[i]即a[i]
// * 我们可以发现&a[i]和a[i]在数值上是相等的.
// * 这是为什么呢?可以这么理解:
// * 还是要从二维数组的本质说起:
// * 1 二维数组的每个元素是个一维数组
// * 2 把二维数组a[i][j]可以看成是有i个元素的一维数组a[i]
// *   那么a[0],a[1]....a[i]都是一维数组
// * 既然a[i]是一维数组,那么&a[i]是一维数组地址块的首地址
// * a[i]是一维数组第一个元素的首地址.
// * 所以他们在数值上是相等的,但是含义不同.
// *
// * 每个元素的地址表示用指针和数组名表示为a+i,其值为*(a+i)
// * 我们可以发现a+i和*(a+i)在数值上是相等的
// * int array[3][4] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
// * 它在内存中存在方式如下:
// * 0X00F8FE4C    1    2    3    4
// * 0X00F8FE5C    5    6    7    8
// * 0X00F8FE6C    9   10   11   12
// * 可以明确的一点:这一块内存地址是连续的,即每个元素之间也是紧挨着存放的.
// * 那么a[3]可以看成{0X00F8FE4C,0X00F8FE5C,0X00F8FE6C}
// * 当然这个数组的首地址也就是0X00F8FE4C
// * a+i表示第i个元素的地址.
// * *(a+i)是其对应的值
// *
// *
// * 那么获取每个一维数组里第j个元素的地址的两种方式就分别是:
// * a[i]+j和*(a+i)+j
// * 那么获取每个一维数组里第j个元素的内容的两种方式就分别是:
// * *(a[i]+j)和*(*(a+i)+j)
// * 所以可以说:
// * a[i]+j和*(a+i)+j等价
// * *(a[i]+j)和*(*(a+i)+j)等价
// */
//void test2(){
//
//	int i=9527;
//	int *p=&i;
//	printf("&i=%x,p=%x,&p=%d\n",&i,&(*p),*p);
//
//	int a[3][4] = { { 0, 1, 2, 3 },
//				    { 4, 5, 6, 7 },
//				    { 8, 9, 10, 11 } };
//
//	//获取a[3]每个元素地址和其对应的内容的两种方式:
//	//&a[i],a[i]和a+i,*(a+i)
//	printf("&a[0]=%x,a[0]=%x <==> a+0=%x,*(a+0)=%x\n",&a[0],a[0],a+0,*(a+0));
//	printf("&a[1]=%x,a[1]=%x <==> a+1=%x,*(a+1)=%x\n",&a[1],a[1],a+1,*(a+1));
//	printf("&a[2]=%x,a[2]=%x <==> a+2=%x,*(a+2)=%x\n",&a[2],a[2],a+2,*(a+2));
//	printf("==========\n");
//
//	//访问第二行的第三个元素:
//	printf("a[1]+2=%x,*(a[1]+2)=%d\n",a[1]+2,*(a[1]+2));
//	printf("*(a+1)+2=%x,*(*(a+1)+2)=%d\n",*(a+1)+2,*(*(a+1)+2));
//
//	printf("==========\n");
//}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

谷哥的小弟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值