系统环境:Ubuntu 18.04.5 LTS
GCC 版本:7.5.0
没有与内存分配函数配套使用
??对一个不是由malloc 、realloc 、calloc 等内存分配函数得到的指针进行内存释放操作
int main(void)
int arr[10] = {0};
free(arr);
return 0;
}
这种是比较容易发现和排除的,一般在编译阶段就会弹出警告信息
有点奇怪的是为什么不直接提示错误,而仅仅只是警告,难道这种写法还有其他妙用嘛?
运行后一样会弹出常规的段错误提示
重复释放
??对一个指针进行两次及以上的free 操作,如下
int main(void)
{
int i;
int *arr = NULL;
arr = (int *)malloc(sizeof(int) * 10);
free(arr);
free(arr);
return 0;
}
这是因为free 函数只对指针指向的内存空间执行释放操作,并不会重置指针的内容,这可以通过打印指针内容观察得知
int main(void)
{
int *arr = NULL;
printf("%p\n", arr);
arr = (int *)malloc(sizeof(int) * 10);
printf("%p\n", arr);
free(arr);
printf("%p\n", arr);
return 0;
}
执行结果如下,可以看到释放后指针的内容还是原内存空间的地址,如果再对这片内存空间进行释放操作就会报错
此时报错信息中除了常规的core dumped 之外,还会给出明确的重复释放提示
预防措施
??实际代码中两个free 之间可能相隔比较远,或是分布在比较杂乱的条件分支,诸如此类的难以察觉的情况,不过由于free 不会对空指针NULL 做相关操作,因此可以在每次free 后都将指针置为NULL 来预防重复释放的报错
不过为了保证代码质量,非必须的重复free 还是要删掉的
int main(void)
{
int i;
int *arr = NULL;
arr = (int *)malloc(sizeof(int) * 10);
free(arr);
arr = NULL;
free(arr); // no error
return 0;
}
指针指向改变
??这一错误的原因网上有不少相关解答,大意就是分配内存时,所给的内存块前后各有一段用来存储内存分配的信息,free 则根据这些信息执行对应的内存释放操作,这应该就是为什么malloc 等内存分配的时候要指定内存空间大小,但是free 的时候却不需要这一参数。所以,如果改变了指针的值,free 读取不到对应的信息就会报错,典型的错误代码如下所示
int main(void)
{
int *arr = NULL;
printf("%p\n", arr);
arr = (int *)malloc(sizeof(int) * 10);
printf("%p\n", arr);
arr = arr + 1;
printf("%p\n", arr);
free(arr);
arr = NULL;
printf("%p\n", arr);
return 0;
}
这里加了些调试语句,执行结果如下图,可以看到,传递给free 的函数指针与一开始分配的指针不一样了,报错提示invalid pointer ,与前两种错误的提示信息不完全一样,倒是可以作为一定的 debug 参考
调试方式
??如上述代码所示,加入一些指针内容打印的调试语句,检查分配内存时的指针内容与释放内存时的指针内容是否一致
内存越界
??分配内存空间比实际使用内存空间要小,产生内存访问越界,如果依照前述内存分配函数的操作进行理解,或许可以视为破坏了分配内存空间的结尾那一段内存分配信息,因此free 会报错。但是实际测试时发现,free 似乎对这种越界有一定的”容错率“,测试代码如下
int main(void)
{
int i;
long *arr = NULL;
const int arr_len = 10;
int test_arr_len = 10;
while (1) {
arr = (long *)malloc(sizeof(long) * arr_len);
for (i = 0; i < test_arr_len; ++i) {
arr[i] = i;
}
free(arr);
arr = NULL;
test_arr_len *= 10;
printf("test array length = %d\n", test_arr_len);
}
}
编译执行结果如下,可以看到,数组长度只分配了10,但是直到读写长度为1000000时free 才出现报错
但是,在编写实际代码的时候发现,这个”越界容忍度“应该是与程序运行规模有关,如果程序其他变量已经分配了不少内存空间,可能稍微出现一点越界就会报错。并且,更为麻烦的是,这种情况下越界可能会影响到其他变量的free ,比如说A 变量越界了,B 变量没越界,有可能B 变量先于A 进行free 时也会报错,因此,在检查这类错误时,如果free 出错的变量并没有问题的话,那就需要检查其他分配了内存空间的变量,如果把动态内存按照堆 的单向增长来理解的话,检查的顺序可以从free 出错的变量往前回溯,找较早分配内存的变量。
调试方式
??按顺序挨个检查
|