通过汇编理解返回char p []和char *p 中P的区别

用两个程序帮助理解返回char p []和char *p 中p的区别。

第一个程序test01.cpp的源代码:

/*
 *filename:test01.cpp
 */
#include <stdio.h>   
char *returnStr()
{
	char *p = "hello world!";
	return p;
}
int main()
{
	char *str;
	str = returnStr();
	printf("%s\n", str);
	return 0;
}

test001.cpp的运行结果:hello world!


第二个程序test002.cpp的源代码

/*
 *filename:test02.cpp
 */
#include <stdio.h>   
char *returnStr()
{
	char  p[] = "hello world!";
	return p;
}
int main()
{
	char *str;
	str = returnStr();
	printf("%s\n", str);
	return 0;
}

返回的结果是乱码的:烫烫烫烫烫烫烫烫,


两个程序代码上的区别在于:

test01.cpp的是

char *p = "hello world!";

test02.cpp的是:

char p[] = "hello world!";

网上解释的是:

test01.cpp程序返回的是指向常量区的地址。程序返回后,常量区地址的内容未变,所以能正常返回“hello world!”

test02.cpp程序返回的是指向的是栈空间的地址,函数返回后,栈空间被释放,返回的地址所指的内容不确定,所以会出现此问题。


以上的解释是能和结果吻合的,但是心中还是有个疑问,等号右边都是一样的“hell world!”,为什么char *p中的p为常量区的地址,而char p[ ]中的p为栈区的地址。于是vs 弄起,看下看下两个程序的关键部分的汇编(前不久自学的汇编,菜鸟一个)。

先看下test02.cpp部分代码的汇编。

char  p[] = "hello world!";
return p;
上面两行代码通过vs反汇编看到的汇编代码:
      7: 	char  p[] = "hello world!";
00073D88 A1 30 6B 07 00       mov         eax,dword ptr ds:[00076B30h]  
00073D8D 89 45 E8             mov         dword ptr [ebp-18h],eax  
00073D90 8B 0D 34 6B 07 00    mov         ecx,dword ptr ds:[00076B34h]  
00073D96 89 4D EC             mov         dword ptr [ebp-14h],ecx  
00073D99 8B 15 38 6B 07 00    mov         edx,dword ptr ds:[00076B38h]  
00073D9F 89 55 F0             mov         dword ptr [ebp-10h],edx  
00073DA2 A0 3C 6B 07 00       mov         al,byte ptr ds:[00076B3Ch]  
00073DA7 88 45 F4             mov         byte ptr [ebp-0Ch],al  
     8: 	return p;
00073DAA 8D 45 E8             lea         eax,[ebp-18h]  
     9: }
从上面看可知,编译器把ds:[00076B30h](备注:可通过vs查看到此时ds寄存器的值为0)的13个字节(加上最后的‘‘\0’)先通过复制到寄存器(一共分4次复制,因为eax、ecx、edx均为4个字节大小,最后一个al为1个字节大小),然后再把寄存器的值复到[ebp-18h](可通过vs查看到此时EBP = 0055F998)地址开始的栈空间(从[ebp-18h]到[ebp-0Ch]  正好是13个字节的空间)。

我们可以看下ds:[00076B30h]内存地址开始的内容:



再看下[ebp-18h]内存地址的空间:



从上面的内容可以看出,同样是“helloword”字符串,他们的地址是不一样的。也就是说,下面这段代码:

char  p[] = "hello world!";
return p;
编译器会重新开辟一个栈空间,把数据的段的“hello world!”复制到栈空间,然后再把这段内容的栈空间的起始地址返回。


再看下test01.cpp关键代码:

char  *p = "hello world!";
return p;

其对应的汇编

     7: 	char  *p = "hello world!";
00DB170E C7 45 F8 30 6B DB 00 mov         dword ptr [ebp-8],0DB6B30h  
     8: 	return p;
00DB1715 8B 45 F8             mov         eax,dword ptr [ebp-8]  
     9: }
0DB6B30h就是存储“hello world!”的数据段起始地址。



也就是说把数据段的地址复制到一个16字节的内存空间(虽然也是栈空间,因为指针变量p占用的是栈空间),再把这段栈空间的值(其实就是数据段地址)复制给eax。没有重新开辟13个栈空间去存储“hello world!”字符串,只是存储地址而已。所以返回的直接就是常量区“hello world!”的地址。

总结:

char  *p = "hello world!";
return p;
以上的第一行代码不会重新开辟栈空间去存储字符串"hello world!",开辟的栈空间是为了存储常量区"hello world!"的起始地址,返回的也是常量区"hello world!"的起始地址p。

char  p[] = "hello world!";
return p;

以上的第一行代码会开辟栈空间去存储字符串"hello world!",返回的是在栈中存储这段字符串的起始地址,即返回的栈空间的地址p


                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值