越界访问的一般提示是 “access violation reading location 0xXXXXXXXX”,翻译过来就是“内存越界访问”。
这个提示和一般提示的不同之处是,程序不会停在越界访问的错误行上,
而是会停在分配或释放内存的行上,于是就需要我们自己去找到越界行。
那要怎么找到越界行呢?这里说一个较为实际的办法:
首先根据 函数调用栈 (Call Stack) 这个工具,找到内存越界访问的那个函数。
例如:
f()
{
#define MAXLIM 100
#define MAXLEN 10
int *p = (int *)calloc(MAXLEN, sizeof(int));
int *q = (int *)calloc(MAXLIM, sizeof(int));
int i;
for(i = 0; i < MAXLIM; i++)
p[i] = 1;
free(q);
free(p); // 程序将在执行这一行后提示一个对话框,同时 Output 窗口中会输出一行 “access violation reading location 0xXXXXXXXX”
}
然后按照下述法则调试代码,保证可以找到内存越界访问:
1. 注释 (Comment) 大量连续源代码,只保留成对的 *alloc 和 free,运行看是否还提示越界。然后进行下一步。
2. 若越界访问,那么注释更大部分源代码。重复此步,直至无越界访问。越界访问行必然在最后两次注释的增加注释里。
若无越界访问,那么注释更小部分源代码。重复此步,直至越界访问。越界访问行必然在最后两次注释的减少注释里。
下面我们试试这个办法。
首先注释大量代码。我们把整个函数都注释了,只保留两个宏定义:
f()
{
#define MAXLIM 100
#define MAXLEN 10
//int *p = (int *)calloc(MAXLEN, sizeof(int));
//int *q = (int *)calloc(MAXLIM, sizeof(int));
//int i;
//for(i = 0; i < MAXLIM; i++)
//p[i] = 1;
//free(q);
//free(p);
}
这样子显然没有任何问题。于是我们注释更小部分代码:
f()
{
#define MAXLIM 100
#define MAXLEN 10
int *p = (int *)calloc(MAXLEN, sizeof(int));
int *q = (int *)calloc(MAXLIM, sizeof(int));
//int i;
//for(i = 0; i < MAXLIM; i++)
//p[i] = 1;
free(q);
free(p);
}
调试发现也没有问题。那么再注释更小部分的代码。for 循环,虽然不容易用“直到换行符的注释(//)”,注释,但是还是可以用“从/*符到*/符的注释”,注释。
f()
{
#define MAXLIM 100
#define MAXLEN 10
int *p = (int *)calloc(MAXLEN, sizeof(int));
int *q = (int *)calloc(MAXLIM, sizeof(int));
int i;
for(i = 0; i < MAXLIM; i++)
/*p[i] = 1*/;
free(q);
free(p);
}
调试后依然没有问题。那么继续减小注释范围。这时发现已经没有可以注释的了,那么就不注释,也就是原来代码了。
原来代码运行必然会提示越界访问的。于是可以肯定地说,越界访问出现在 "p[i] = 1;" 上。正是这个索引 i 超过了允许访问的界限。
于是我们要比较一下 i 的访问范围和 p 的允许范围。
i 的访问范围是 0 ... MAXLIM - 1
p 的允许范围是 0 ... MAXLEN - 1
而 MAXLEN < MAXLIM ,于是越界。这时原因查出,只需根据需要,减小赋值范围或增大分配内存即可。
f()
{
#define MAXLIM 100
#define MAXLEN 10
int *p = (int *)calloc(MAXLEN, sizeof(int));
int *q = (int *)calloc(MAXLIM, sizeof(int));
int i;
for(i = 0; i < MAXLIM; i++)
p[i] = 1;
free(q);
free(p);
}
其实从 free 能够释放内存这里可以知道,对已分配内存其实像字符串一样已经做过标记,所以释放时才能够知道需要释放到哪儿。
赋值时候不会自动检测是否越界访问,也是为了效率提升。如果一定需要检测,我们可以自己用 assert 宏进行实现:
f()
{
#define MAXLIM 100
#define MAXLEN 10
int *p = (int *)calloc(MAXLEN, sizeof(int));
int *q = (int *)calloc(MAXLIM, sizeof(int));
int i;
for(i = 0; i < MAXLIM; i++)
{
assert(i < MAXLEN);
p[i] = 1;
}
free(q);
free(p);
}