在看《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
结果是段错误。