对程序员来说内存相关的 bug 排查难度几乎和多线程问题并驾齐驱,当程序出现运行异常时可能距离真正有 bug 的那行代码已经很远了,这就导致问题定位排查非常困难,这篇文章将总结涉及内存的一些经典 bug ,快来看看你知道几个,或者你的程序中现在有几个。。。
返回局部变量地址
我们来看这样一段代码:
int fun() {
int a = 2;
return &a;
}
void main() {
int* p = fun();
*p = 20;
}
这段代码非常简单,func 函数返回一个指向局部变量的地址,main 函数中调用 fun 函数,获取到指针后将其设置为 20。
你能看出这段代码有什么问题吗?
问题在于局部变量 a 位于 func 的栈帧中,当 func 执行结束,其栈帧也不复存在,因此 main 函数中调用 func 函数后得到的指针指向一个不存在的变量:
尽管上述代码仍然可以“正常”运行,但如果后续调用其它函数比如funcB,那么指针p指向的内容将被 funcB 函数的栈帧内容覆盖掉,又或者修改指针 p 实际上是在破坏 funcB 函数的栈帧,这将导致极其难以排查的 bug。
int sum(int* arr, int len) {
int sum = 0;
for (int i = 0; i < len; i++) {
sum += *arr;
arr += sizeof(int);
}
return sum;
}
这段代码本意是想计算给定数组的和,但上述代码并没有理解指针运算的本意。
指针运算中的加1并不是说移动一个字节而是移动一个单位,指针指向的数据结构大小就是一个单位。因此,如果指针指向的数据类型是 int,那么指针加 1 则移动 4 个字节(32位),如果指针指向的是结构体,该结构体的大小为 1024 字节,那么指针加 1 其实是移动 1024 字节。
从这里我们可以看出,移动指针时我们根本不需要关心指针指向的数据类型的大小,因此上述代码简单的将arr += sizeof(int)改为arr++即可。
C语言初学者常会犯一个经典错误,那就是从标准输入中获取键盘数据,代码是这样写的:
int a;
scanf("%d", a);
很多同学并不知道这样写会有什么问题,因为上述代码有时并不会出现运行时错误。
原来 scanf 会将a的值当做地址来对待,并将从标准输入中获取到的数据写到该地址中。
这时接下来程序的表现就取决于a的值了,而上述代码中局部变量a的值是不确定的,那么这时:
----------伟大的分割线-----------
PHP饭米粒(phpfamily) 由一群靠谱的人建立,愿为PHPer带来一些值得细细品味的精神食粮!
饭米粒只发原创或授权发表的文章,不转载网上的文章
所发的文章,均可找到原作者进行沟通。