再谈“0xC0000005读取写入位置冲突”——正确使用指针访问内存
在前面一篇文章中,总结了错误使用指针指向一个字符串常量,然后对该字符串常量进行写入操作导致出错:“未经处理的异常:0xC0000005: 读取/写入位置发生访问冲突”。
这篇文章继续使用指针来探索这个问题。
1 测试程序:“异常: 0xC0000005: 写入位置 0xxxxxxxxx 时发生访问冲突”
下面是一个测试程序,主要是字符串的“连接”——strcat函数,malloc有迷惑性。其中,指针p1指向malloc在堆上开辟的连续内存块,数组p3是一种正确的字符连接方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
2 在vs中设置断点并调试
如下图设置好断点后,单步执行到第10行。此时指针变量p1的值是 0x008734c0 ,p2的值是 0x00d75774 ,数组p3的起始地址是 0x0036f928 。
2.1详细查看指针变量p1、p2以及数组p3的内存情况:(第10行)
此时,指针变量p1通过语句 char * p1 = (char *)malloc( sizeof(char)*20 );
指向起始地址为 0x008734c0 到 地址 0x008734d3 的内存块(大小为 20 字节)。
指针变量 p2 通过执行 char * p2 = "efgh";
指向起始地址为 0x00d75774 的字符串“efgh”(共占 4 字节)。
数组 p3 通过 char p3[20]= "igkl";
开辟了起始地址为 0x0036f928 的内存块(共占 4 字节),存储字符串“igkl”。
2.2继续单步调试并查看内存情况:(第11行)
上述变量初始化完毕后,单步执行到第 11 行的断点。这时指针变量p1由最初的指向 0x008734c0 变成指向字符串“abcd”的地址 0x00d7576c 。p2 的地址仍不变(0x00d75774),p3的地址也不变(0x0036f928)。
详细查看指针变量p1的内存情况:起始地址是 0x00d7576c ,该内存块存储的字符串是“abcd”。
2.3继续单步调试并查看内存情况:(第12行)
然后,单步执行到第 12 行的断点,出现错误:test.exe 中的 0x0f85d2e9 (msvcr100d.dll) 处有未经处理的异常: 0xC0000005: 写入位置 0x00d75770 时发生访问冲突。
也即在执行第11行的 strcat( p1, p2 );
语句时,发生内存访问错误。该语句是把作为src的指针变量p2(0x00d75774)指向的内存块中的字符串“efgh”拷贝一份,然后复制到作为dst的指针变量p1(0x00d7576c)所指向内存块中的的字符串常量“abcd”后面。调用堆栈可以看到,当执行 msvcr100d.dll!strcat(unsigned char * dst, unsigned char * src)
对应的strcat.asm汇编文件中第178行语句 mov [edi], edx
时出错。
3 原因分析
其实,通过上面的单步调试,我们也已经知道原因了。现总结一下:因为指针变量p1(0x00d7576c)所指向的字符串常量“abcd”后面,也即字符'd'的地址是 0x00d7576c+3=0x00d7576f 。
而它后面以地址 0x00d75770 开始的内存块是不属于指针变量p1的,没有访问权限,所以把拷贝自指针变量p2(0x00d75774)指向的字符串“efgh”到以 0x00d75770 为起始地址的内存块中会报错。可以把语句 p1 = "abcd";
改成 p1 = p3;
,然后直接使用 strcat( p1, p2 );
,这样是合法的。
Comments »