转自:http://www.typecodes.com/cseries/pointermemaccess0xC0000005.html
在前面一篇文章中,总结了错误使用指针指向一个字符串常量,然后对该字符串常量进行写入操作导致出错:“未经处理的异常:0xC0000005: 读取/写入位置发生访问冲突”。这篇文章继续使用指针来探索这个问题。
1 测试程序:“异常: 0xC0000005: 写入位置 0xxxxxxxxx 时发生访问冲突”
下面是一个测试程序,主要是字符串的“连接”——strcat函数,malloc有迷惑性。其中,指针p1指向malloc在堆上开辟的连续内存块,数组p3是一种正确的字符连接方法。
/**
* @FileName : pointermemaccess0xC0000005.c
* @Author : vfhky http://www.typecodes.com 2014.08.25 20:00
* @Functions: 不正确使用指针p1导致出现“异常: 0xC0000005”和正确使用数组实现字符串的连接
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main( int argc, char * argv[] )
{
char * p1 = (char *)malloc( sizeof(char)*20 );
char p3[20]= "igkl";
char * p2 = "efgh";
p1 = "abcd"; //p1指向字符串"abcd"的首地址, 而不是把"abcd"拷贝到malloc开辟的内存块中
strcat( p1, p2 ); //报错: 0xC0000005异常,访问冲突
strcat( p3, p2 ); //正确
printf( "p1=[%s], p2=[%s]\n", p1, p2 );
printf( "p3=[%s], p2=[%s]\n", p3, p2 );
//释放内存
free( p1 );
p1 = NULL;
return 0;
}
</pre><pre name="code" class="cpp">
<h3><span style="font-family: Arial, Helvetica, sans-serif;">2 在vs中设置断点并调试</span></h3>如下图设置好断点后,单步执行到第10行。此时指针变量p1的值是 0x008734c0 ,p2的值是 0x00d75774 ,数组p3的起始地址是 0x0036f928 。
<img src="https://img-blog.csdn.net/20141205215314511?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvRmx5aW5nQmlyZF9TWEY=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />
<span style="font-family: Arial, Helvetica, sans-serif;">
</span>
2.1详细查看指针变量p1、p2以及数组p3的内存情况:(第10行)
此时,指针变量p1通过语句 char * p1 = (char *)malloc( sizeof(char)*20 ); 指向起始地址为 0x008734c0 到 地址 0x008734d3 的内存块(大小为 20 字节)。
<img src="https://img-blog.csdn.net/20141205215447003?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvRmx5aW5nQmlyZF9TWEY=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />
指针变量 p2 通过执行 char * p2 = "efgh"; 指向起始地址为 0x00d75774 的字符串“efgh”(共占 4 字节)。
<img src="https://img-blog.csdn.net/20141205215550296?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvRmx5aW5nQmlyZF9TWEY=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />
<span style="font-family: Arial, Helvetica, sans-serif;">
</span>
<span style="font-family: Arial, Helvetica, sans-serif;">数组 p3 通过 char p3[20]= "igkl"; 开辟了起始地址为 0x0036f928 的内存块(共占 4 字节),存储字符串“igkl”。</span>
<img src="https://img-blog.csdn.net/20141205215631773?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvRmx5aW5nQmlyZF9TWEY=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />
2.2继续单步调试并查看内存情况:(第11行)
上述变量初始化完毕后,单步执行到第 11 行的断点。这时指针变量p1由最初的指向 0x008734c0 变成指向字符串“abcd”的地址 0x00d7576c 。p2 的地址仍不变(0x00d75774),p3的地址也不变(0x0036f928)。
<img src="https://img-blog.csdn.net/20141205215813797?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvRmx5aW5nQmlyZF9TWEY=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />
详细查看指针变量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 );,这样是合法的。