menu Alkaid #二进制初学者 / 网络安全 / 大龄CTF退役选手
从汇编语言的角度看清数组名和指针变量的区别
198 浏览 | 2017-10-02 | 分类:心路历程,汇编语言 | 标签:汇编

char a[]=”hello”;
char *p=”hello”;

同:

都可以用名字输出字符串首地址
printf(“%p %p\n”,a,p);

都可以用名字输出字符串内容
printf(“%s %s\n”,a,p);

都可以用下标操作
a[1] 和 p[1]都对应字符串第1个字符

最大的不同:

p + 1 和 a+1 都对应第一个字符的地址

但是:

可以执行 p = p + 1 或 p = a + 1;
却不可以执行 a = a + 1;
即,赋值运算的左操作数不能是数组名

分析

为了方便,用8086汇编来说明一下:

数组名和变量名都可以看作汇编语言里的标号,如下:

只需要关注第9、10、20行即可

(1)对应的内存空间大小不一样

第9行定义了6个字节的大小(即数组),第10行定义了2个字节(即标号p对应的空间)。

char *p = “hello”;
虽然 a和p 指向的都是一个字符串,但是 a 对应的空间中直接存储了字符串“hello”( ‘$’是字符串结束符,相当于‘\0’), 而p 对应的空间中存储的是字符串的首地址。

p = “hello” 相当于汇编代码中的第20行,获取到”hello”的首地址存到p中。(间接指向字符串)

这也就说明了为什么 sizeof(a) 和 sizeof(p) 计算出的大小是不一样的。

(2)名称(标号)的区别

从汇编中可以看出,CPU 只能直接操作固定大小单元的数据(字节,字),因为数据的操作是要经过寄存器的,寄存器只能是8位寄存器(AL、BL…)、16位寄存器(AX、BX…)…

指针变量p 中存的是地址值,只需要分配 dw 大小(事实上在64位系统中并不是2个字节)。所以,指针变量可以被操作,加、减、赋值运算等。
比如:p = p + 1 (地址值加1)
add p,1

但是字符串是单个字符组成的,每个字符都占8个字节单元,CPU是无法直接对这么一大块内存进行操作的,比如“hello”+1 你觉得这个值该是什么呢。对于字符串是利用首地址进行操作的,根据标号a 可以获取到字符串的首地址,a+n 可以获取到相对首地址偏移n 的地址。使用offset 获取有效地址,如 mov p,offset a+3 ,等价于 p=a+3.

所以汇编中,p+3 和 offset a+3 是等价的,仅仅是a+3对应的是相对首地址偏移量为3的字符。

p = p+1
可以用add p,1 表示;

但是 a=a+1
若用add a,1 表示,则实际含义是:a 是首地址,对应第0个字符’h’, 加1 则表示第0个字符加1,字符串变成了”iello”。这其实是 a[0] = a[0]+1。
用汇编并不能实现 地址值+1 然后赋值到 a 中的效果,所以不能执行a = a+1 。

下标操作就是相对于标号偏移n对应的内存单元,a[2],p[2] 即a+2和p+2对应的单元(这里是一个字节)。

(3)用常量字符串初始化字符数组和指针变量

char a[]="hello"; 
char *p="hello";

这两条语句似乎没啥区别,都是初始化了一个字符串。但是,经过上面的分析可以看出这两条语句的区别是很明显的。

字符数组的初始化其实是定义了6个字节的内存空间,
然后把”hello”的每个字符依次放进去,a[0]=’h’,a[1]=’e’。。。

而指针变量的初始化是,先定义了比如2个字节的单元,然后把常字符串的首地址存进去。

所以,可以用下标操作,如 a[1] = ‘b’ 来更改字符串的内容。 但是,指针变量是间接的指向常字符串,他是通过常字符串的地址来操作字符串,在c语言中,常字符串是只读的,不允许修改,一般编译的时候把常字符串放进了文本段中。所以,不能通过指针变量来修改常字符串,比如 p[1] = ‘b’ 。他产生的结果是未定义的,未定义即编译器可能报错或产生了异常结果。

另外:
char *p = NULL;
p = “hello”;
这样也不可以修改常字符串,道理和上面是一样的。

转自http://blog.csdn.net/lyh__521/article/details/50069867

温柔正确的人总是难以生存,因为这世界既不温柔,也不正确

发表评论

email
web

全部评论 (暂无评论)

info 还没有任何评论,你来说两句呐!