TypeCodes

再谈“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
/**
 * @FileName :  pointermemaccess0xC0000005.c
 * @Author   :  vfhky https://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;
}
2 在vs中设置断点并调试

如下图设置好断点后,单步执行到第10行。此时指针变量p1的值是 0x008734c0 ,p2的值是 0x00d75774 ,数组p3的起始地址是 0x0036f928 。

在vs中设置断点并单步调试

2.1详细查看指针变量p1、p2以及数组p3的内存情况:(第10行)

此时,指针变量p1通过语句 char * p1 = (char *)malloc( sizeof(char)*20 ); 指向起始地址为 0x008734c0 到 地址 0x008734d3 的内存块(大小为 20 字节)。

初始化指针变量p1的内存地址

指针变量 p2 通过执行 char * p2 = "efgh"; 指向起始地址为 0x00d75774 的字符串“efgh”(共占 4 字节)。

初始化指针变量p2的内存地址

数组 p3 通过 char p3[20]= "igkl"; 开辟了起始地址为 0x0036f928 的内存块(共占 4 字节),存储字符串“igkl”。

初始化数组p3的内存块

2.2继续单步调试并查看内存情况:(第11行)

上述变量初始化完毕后,单步执行到第 11 行的断点。这时指针变量p1由最初的指向 0x008734c0 变成指向字符串“abcd”的地址 0x00d7576c 。p2 的地址仍不变(0x00d75774),p3的地址也不变(0x0036f928)。

指针变量p1指向字符串“abcd”的首地址

详细查看指针变量p1的内存情况:起始地址是 0x00d7576c ,该内存块存储的字符串是“abcd”。

指针变量p1的内存情况

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 时出错。

未经处理的异常: 0xC0000005: 写入位置 0x00d75770 时发生访问冲突

strcat.asm汇编文件mov指令出错

3 原因分析

其实,通过上面的单步调试,我们也已经知道原因了。现总结一下:因为指针变量p1(0x00d7576c)所指向的字符串常量“abcd”后面,也即字符'd'的地址是 0x00d7576c+3=0x00d7576f 。

而它后面以地址 0x00d75770 开始的内存块是不属于指针变量p1的,没有访问权限,所以把拷贝自指针变量p2(0x00d75774)指向的字符串“efgh”到以 0x00d75770 为起始地址的内存块中会报错。可以把语句 p1 = "abcd"; 改成 p1 = p3;,然后直接使用 strcat( p1, p2 );,这样是合法的。

地址 0x00d75770 不能写入操作

打赏支持

Comments »