指针和数组相同吗?

在看《C专家编程》第四章时,没看明白,只知道指针和数组在引用的时候是不一样的(这只是两者区别之一)。

今天做了个实验,终于发现了其中的奥妙。根据引用方法的不同,我分为数组引用和指针引用。

数组引用

情景1.定义为指针,声明为指针

首先有一个文件test6.c,里面只有一个定义:

char *p="abcdefgh";
然后在test7.c里是这样:

#include<stdio.h>
extern char *p;

void main()
{
	printf("%x\n",p);
	printf("p[0]=%c\n",p[0]);
}

这样的话p[0]能够正确打印出来。

情景2.定义为指针,声明为数组

如果把test7.c改成:

#include<stdio.h>
extern char p[];

void main()
{
	printf("%x\n",p);
	printf("p[0]=%c\n",p[0]);
}
如果说数组和指针是一样的话,那输出的结果应当是正确的。但是结果却是乱码。

这种时候最有用的就是汇编了。

分析以上两种情境:

情境1这一版本的汇编是:

printf("p[0]=%c\",p[0]);
	movl	p, %eax
	movzbl	(%eax), %eax
	movsbl	%al,%edx
	movl	$.LC1, %eax
	movl	%edx, 4(%esp)
	movl	%eax, (%esp)
	call	printf
情境2这一版本的则是:

printf("p[0]=%c\",p[0]);
	movzbl	p, %eax
	movsbl	%al,%edx
	movl	$.LC1, %eax
	movl	%edx, 4(%esp)
	movl	%eax, (%esp)
	call	printf
很显然,这两个版本在真正引用时是不一样的。

对于情境1来说,它先将p中的内容(4字节)传送到eax寄存器,然后取到eax寄存器里指向的地址的内容,只取1个字节,再传送到eax寄存器,并将高3个字节置0。这时候eax寄存器里存储的就是p指向的地址的第一个字节里的内容,也就是'a',接下来进行打印。

而对于情境2来说,它认为p是一个数组,它会直接取得低字节的内容。movzbl p,%eax 就是将p中的低字节传入eax,并将高3个字节置0。然后就直接打印出来。

(这里的高低并不一定是p中的高位和低位,而是存储单元中的高位和低位。具体系统有不同的实现方法。可查阅大小端的资料)

很显然,第二种方法打印出来的是p中的内容,结果当然是牛头不对马嘴的。

情景3.定义为数组,声明为数组

test6.c 是:

char p[]="abcdefgh“;

test7.c是:

#include<stdio.h>
extern char p[];

void main()
{
	printf("%x\n",p);
	printf("p[0]=%c\n",p[0]);
}
汇编是:

movzbl	p, %eax
	movsbl	%al,%edx
	movl	$.LC1, %eax
	movl	%edx, 4(%esp)
	movl	%eax, (%esp)
	call	printf

正确打印。

情景4.定义为数组,声明为指针

test6.c里是

char p[]="abcdefgh";
test7.c里是:

#include<stdio.h>
extern char *p;

void main()
{
	printf("%x\n",p);
	printf("p[0]=%c\n",p[0]);
}

那么相应的汇编代码是:

movl	p, %eax
	movzbl	(%eax), %eax
	movsbl	%al,%edx
	movl	$.LC1, %eax
	movl	%edx, 4(%esp)
	movl	%eax, (%esp)
	call	printf

它将p的内容,就是p数组里的'abcd‘传到了eax,然后取得'abcd’指向的内容,显然这个地址不是当前进程可以访问的,会出现段错误的提示。

上面都是以数组的方式引用数据,下面以指针的方式引用数据,看看有什么区别吗?

指针引用

指针引用情境1:定义为指针,声明为指针

test6.c:

char *p="abcdefgh";

test7.c:

#include<stdio.h>
extern char *p;

void main()
{
	printf("%x\n",p);
	printf("*(p)=%c\n",*(p));
}

汇编:

	movl	p, %eax
	movzbl	(%eax), %eax
	movsbl	%al,%edx
	movl	$.LC1, %eax
	movl	%edx, 4(%esp)
	movl	%eax, (%esp)
	call	printf

打印正确。

指针引用情境2:定义为指针,声明为数组

test6.c

char *p="abcdefgh";

test7.c

#include<stdio.h>
extern char p[];

void main()
{
	printf("*(p)=%c\n",*(p));
}
汇编:

	movzbl	p, %eax
	movsbl	%al,%edx
	movl	$.LC0, %eax
	movl	%edx, 4(%esp)
	movl	%eax, (%esp)
	call	printf

打印错误。

指针引用情境3:定义为数组,声明为数组

直接列出汇编代码:

	movzbl	p, %eax
	movsbl	%al,%edx
	movl	$.LC0, %eax
	movl	%edx, 4(%esp)
	movl	%eax, (%esp)
	call	printf

打印正确。

指针引用情境4:定义为数组,声明为指针

汇编代码:

movl	p, %eax
	movzbl	(%eax), %eax
	movsbl	%al,%edx
	movl	$.LC0, %eax
	movl	%edx, 4(%esp)
	movl	%eax, (%esp)
	call	printf

结果是段错误。

所以,我最后得出的断定是:
数组和指针在访问数据的时候并不相同,这与它们访问时使用的引用方法(数组引用或指针引用)无关,而与它们在文件中的声明有关。避免错误的方法就是:一致地声明和定义数组和指针。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值