下面是从汇编级别来看指针的操作,查看因指针操作而导致的问题。
#include "stdafx.h"
#include <stdlib.h>
void allocate(int *p)
{
p = (int*)malloc(sizeof(int));
*p = 5;
}
int _tmain(int argc, _TCHAR* argv[])
{
int* p = NULL;
allocate(p);
printf("p = %d\n", *p);
system("pause");
return 0;
}
按照上述的代码,在压栈操作时,将p的值0压入;并且p在主函数中的地址为0x0013ff60;
但是在函数内部,p的地址为0x0013fe8c;因为p作为局部变量,所以在栈上开辟;
由此可见,函数中对于p的赋值,不可能改变主函数中的值;
那么要进行修改的话,只要将p的地址传入即可,也即指针的指针。
void allocate2(int **p)
{
*p = (int*)malloc(sizeof(int));
**p = 5;
}
int _tmain(int argc, _TCHAR* argv[])
{
int* p = NULL;
allocate2(&p);
printf("p = %d\n", *p);
system("pause");
return 0;
}
经过上述的修改后,传入的是p的地址:0x13ff60,
15: *p = (int*)malloc(sizeof(int));
0041145E 8B F4 mov esi,esp
00411460 6A 04 push 4
00411462 FF 15 CC 82 41 00 call dword ptr [__imp__malloc (4182CCh)]
00411468 83 C4 04 add esp,4
0041146B 3B F4 cmp esi,esp
0041146D E8 E7 FC FF FF call @ILT+340(__RTC_CheckEsp) (411159h)
00411472 8B 4D 08 mov ecx,dword ptr [p]
00411475 89 01 mov dword ptr [ecx],eax (1)
16: **p = 5;
00411477 8B 45 08 mov eax,dword ptr [p]
0041147A 8B 08 mov ecx,dword ptr [eax]
0041147C C7 01 05 00 00 00 mov dword ptr [ecx],5
语句1将malloc申请空间的地址赋给13ff60地址空间,也即13ff60内存中存有指向内存空间的地址值,比如A;
00411477 8B 45 08 mov eax,dword ptr [p] // eax = 13ff60;
0041147A 8B 08 mov ecx,dword ptr [eax] // ecx = A;
0041147C C7 01 05 00 00 00 mov dword ptr [ecx],5 // 将5赋给地址为A的内存;
后面的*p;因为传入的是13ff60地址,修改的也是这个地址的内存值,所以主程序中的p值存储的就是allocate2函数中申请的那个空间。
注:
在函数内部申请空间的做法,并且将回传的做法不好,因为申请空间的代码和释放空间的代码不在一个函数内,造成程序员的负担,很容易内存泄露;
下面又是一个查找错误的问题:
class useStr{
public:
void setStr1(){
pc = (char*)malloc(4);
pc = "abc";
}
void setStr2(){
char a[4] = "abc";
pc = a;
printf("in setStr2, %s\n", pc);
}
char* getStr(){
return pc;
}
private:
char* pc;
};
int _tmain(int argc, _TCHAR* argv[])
{
useStr us1, us2;
us1.setStr1();
printf("%s\n", us1.getStr());
us2.setStr2();
printf("%s\n", us2.getStr());
system("pause");
return 0;
}
上述的第一个输出为正确的abc,而后者输出则是乱码。
但奇怪的是后者在代码内进行输出时,却是正常的。可能的问题就是pc的值发生变化,或者pc所指向的内容发生变化。
经过ollydbg的调试,us2.setStr2()中p指向的”abc”设置在栈上,但是栈中的值在调用后续的us2.getStr()时,其中的内容被替换掉。因此造成最终的输出值发生改变。
具体改变”abc”值的代码主要是debug版本为调试在栈中添加的”CC”等特征码。然后在调用printf时,该栈处的内容又被修改。
有趣的是,当我们将版本设置为release版本后,上述的问题就不存在了,主要的原因是栈中的信息没有因为debug中的信息填充而被修改,或者因为其他函数的调用而被修改。