数组指针和指针数组

指针数组主体是一个数组,数组中每一个元素为一指针。

数组指针主体是一个指针,这个指针指向一个数组。

本文将基于实际的例子,对有关数组和指针的值以及地址进行输出,并根据输出理解。

实际例子如下:

int * e[2]; //指针数组,数组e包含两个整型指针

int (*u)[2];//数组指针,指针u指向一个二维整型数组

这里对有关的值进行输出,源码如下:

#include<stdio.h>
#include<malloc.h>

int main()
{
	int count = 2;

	//指针数组
	int *e[2];
	e[0] = (int*)malloc(sizeof(int) * count);
	e[1] = (int*)malloc(sizeof(int) * count);
	
	//数组指针
	int (*u)[2];
	u = (int(*)[2])malloc(sizeof(int(*)[2]) * count);

	u[0][0] = 1;
	e[0][0] = 1;
	
	printf("\n");
	printf("----------------------");							 printf("----------------------\n");
	printf("%9s %8d", "e:", e);				  printf("   |   "); printf("%9s %8d\n", "u:", u);
	printf("%9s %8d", "&e:", &e);			  printf("   |   "); printf("%9s %8d\n", "&u:", &u);
	printf("%9s %8d", "*e:", *e);			  printf("   |   "); printf("%9s %8d\n", "*u:", *u);
	printf("%9s %8d", "&(*e):", &(*e));		  printf("   |   "); printf("%9s %8d\n", "&(*u):", &(*u));
	printf("----------------------");							 printf("----------------------\n");
	printf("%9s %8d", "e[0]:", e[0]);		  printf("   |   "); printf("%9s %8d\n", "u[0]:", u[0]);
	printf("%9s %8d", "&e[0]:", &e[0]);		  printf("   |   "); printf("%9s %8d\n", "&u[0]:", &u[0]);
	printf("%9s %8d", "*e[0]:", *e[0]);		  printf("   |   "); printf("%9s %8d\n", "*u[0]:", *u[0]);
	printf("%9s %8d", "&(*e[0]):", &(*e[0])); printf("   |   "); printf("%9s %8d\n", "&(*u[0]):", &(*u[0]));
	printf("----------------------");							 printf("----------------------\n");
	printf("%9s %8d", "e[0][0]:", e[0][0]);   printf("   |   "); printf("%9s %8d\n", "u[0][0]:", u[0][0]);
	printf("----------------------");							 printf("----------------------\n");
	printf("\n");

	return 0;
}

代码运行环境:32位win7,VS2008

最终代码的运行结果如下图所示:


从上述结果我们可以看到一些(指针数组e)和(数组指针u)的内存分布情况,图示如下:


从图中可以看到,数组e由地址4389728开始,而指针u的地址则在4389716处。

4389728 [1192176] 与 4389716[1192288] 在本质上都是一个指针,可以看到它们存储的内容(也即"[]"内的数字)都是一个地址。不过,存储在4389728处的指针,指向的是一个整数;而存储在4389716处的指针则指向的是一个数组。

而上图中的双向箭头则指示出由地址4389728开始的数组e,和由地址1192288开始的数组(该数组没有自身的名字,而是由指针u指向的)都是二维的数组,不过,数组e保存的是两个指针,而另一数组则保存的是两个整数。

现在,我们来依次看一下,源码的输出。

(1)e和u

u是一个指针,它其中存储的是一个地址,输出结果将u存储的地址1192288输出了,也即printf输出了u这个变量的值。

e是一个数组,printf输出的是数组的起始地址。关于该方面的理解可参考网页:点击打开链接。网页中文章题为“数组编译器实现”,这里截取该文的段首:

"声明一个数组时,编译器将根据声明所指定的元素数量为数组保留内存空间,然后再创建数组名,它的值时一个常量,指向这段空间的起始位置。
数组名是符号地址常量,在编译时求值并存在编译器的符号表里面,其值就是个内存地址;所以可以认为程序没有给a分配空间,数组名只是代表了那个数组空间;与指针不一样,指针指向一块空间,同时指针本身也存储在某个空间;可以认为数组名存在在符号表里,符号表是编译器用的,我们管不到;a和&a值是一样的,本来对常量取地址是非法的,但是标准组织没有定对数组名取地址是非法还是合法,所以因编译器而异,VC是合法的。"

该文还举了一个具体的例子,该例子对理解源码中各个值的输出很有帮助。

(2)&e和&u

u是一个变量,&u取其地址,输出为4389716.

而&e的值(1)中已有说明。也即"标准组织没有定对数组名取地址是非法还是合法,所以因编译器而异,VC是合法的。"可以看到,VC中&e输出了数组起始地址。

(3)*e和*u

直观上看*u是取指针u中存储的值,而也即u存储的地址1192288。不过我们需要注意的是u是指向一个数组的,*u应当是一个数组类型的变量,也即*u和e是类似的。

*e,是从一个数组名中取其值,我想,或者我们可以说"标准组织没有定对数组名取值是非法还是合法,所以因编译器而异,VC是合法的。"可以看到,VC中*e输出了数组起始地址中存储的值,也即对于*e的处理方式和指针是相似的。

(4)&(*e)和&(*u)

这里需要说明的是&(*u)。我们从(3)中知道*u是1192288这个值,那为什么&(*u)不是1192288该值所在的地址4389716呢?这是因为*u代表的是一个数组,而由(1)可知,在VC中对数组取地址的操作,得到的值是数组的起始地址,也即1192288。

而&(*e)则得到的是*e这个值的地址。不同点即在于*e得到的是数组中的第一个变量,是一个普通的变量(我想,这也是VC编译器规定的);而*u得到的是一个数组变量。

(5)e[0]和u[0]

e[0]简单,不再说明。

这里需要注意的是u[0]。按照文“数组编译器实现”中,指针的下标寻址过程来看:

step 1. 在u的内存地址4389716处取得内容:1192288

step 2. 取得下标值0,计算最终地址为:1192288 + 0 * sizeof(int) = 1192288(这里的sizeof(int)不应该理解成整形大小,u是一个指针(e是一个数组),这里应该理解为一个指针的大小,不过指针的大小和int的大小是相同的)

step 3. 在地址1192288取得内容

这里关键在于step 3,“在地址1192288处取得内容”,取的并非是1192288存的那个1,而是从1192288处开始的二维整型数组,也就是u[0]代表的还是一个数组,这也就能理解u[0]的输出为什么是1192288了。

(6)&e[0]和&u[0]

&e[0]简单,不再说明。

结合(5)中的解释,可知&u[0]的输出。

(7)*e[0]和*u[0]

*e[0]即是从指针e[0]中取值。

*u[0]相当于*e(u[0]和e均代表的是二维数组),所以取的是u[0]代表的二维数组起始地址中的值。可以参照(3)中的“可以看到,VC中*e输出了数组起始地址中存储的值”。

最后两种输出就不再解释了。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值