对程序员来说内存相关的 bug 排查难度几乎和多线程问题并驾齐驱,当程序出现运行异常时可能距离真正有 bug 的那行代码已经很远了,这就导致问题定位排查非常困难,这篇文章将总结涉及内存的一些经典 bug ,快来看看你知道几个,或者你的程序中现在有几个。。。
返回局部变量地址
int fun() { int a = 2; return &a;}void main() { int* p = fun(); *p = 20;}
错误的理解指针运算
int sum(int* arr, int len) {
int sum = 0;
for (int i = 0; i < len; i++) {
sum += *arr;
arr += sizeof(int);
}
return sum;
}
解引用有问题的指针
int a;
scanf("%d", a);
如果a的值作为指针指向代码区或者其它不可写区域,操作系统将立刻kill掉该进程,这是最好的情况,这时发现问题还不算很难
如果a的值作为指针指向栈区,那么此时恭喜你,其它函数的栈帧已经被破坏掉了,那么程序接下来的行为将脱离掌控,这样的 bug 极难定位
如果a的值作为指针指向堆区,那么此时也恭喜你,代码中动态分配的内存已经被你破坏掉了,那么程序接下来的行为同样脱离掌控,这样的bug也极难定位
读取未初始化的内存
void add() {
int* a = (int*)malloc(sizeof(int));
*a += 10;
}
如果 malloc 自己维护的内存够用,那么 malloc 从空闲内存中找到一块大小合适的返回,注意,这一块内存可能是之前用过后释放的。在这种情况下,这块内存包含了上次使用时留下的信息,因此不一定为0
如果 malloc 自己维护的内存不够用,那么通过 brk 等系统调用向操作系统申请内存,在这种情况下操作系统返回的内存确实会被初始化为0。 原因很简单,操作系统返回的这块内存可能之前被其它进程使用过,这里面也许会包含了一些敏感信息,像密码之类,因此出于安全考虑防止你读取到其它进程的信息,操作系统在把内存交给你之前会将其初始化为0。
内存泄漏
void memory_leak() {
int *p = (int *)malloc(sizeof(int));
return;
}
引用已被释放的内存
void add() {
int* a = (int*)malloc(sizeof(int));
...
free(a);
int* b = (int*)malloc(sizeof(int));
*b = *a;
}
指针a指向的那块内存释放后没有被 malloc 再次分配出去,那么此时a指向的值和之前一样
指针a指向的那块内存已经被 malloc分配出去了,此时a指向的内存可能已经被覆盖,那么*b得到的就是一个被覆盖掉的数据,这类问题可能要等程序运行很久才会发现,而且往往难以定位。
循环遍历是0开始的
void init(int n) {
int* arr = (int*)malloc(n * sizeof(int));
for (int i = 0; i <= n; i++) {
arr[i] = i;
}
}
指针大小与指针所指向对象的大小不同
int **create(int n) {
int i;
int **M = (int **)malloc(n * sizeof(int));
for (i = 0; i < n; i++)
M[i] = (int *)malloc(m * sizeof(int));
return M;
}
栈缓冲器溢出
void buffer_overflow() {
char buf[32];
gets(buf);
return;
}
操作指针所指对象而非指针本身
void delete_one(int** arr, int* size) {
free(arr[*size - 1]);
*size--;
}
总结
·················· END ··················
关注后回复「1024」,获取海量学习资源