前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >10 个内存引发的大坑,你能躲开几个?(1)

10 个内存引发的大坑,你能躲开几个?(1)

作者头像
桶哥
发布2021-03-18 14:51:27
3840
发布2021-03-18 14:51:27
举报
文章被收录于专栏:PHP饭米粒PHP饭米粒PHP饭米粒

对程序员来说内存相关的 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的值是不确定的,那么这时:

  1. 如果a的值作为指针指向代码区或者其它不可写区域,操作系统将立刻kill掉该进程,这是最好的情况,这时发现问题还不算很难
  2. 如果a的值作为指针指向栈区,那么此时恭喜你,其它函数的栈帧已经被破坏掉了,那么程序接下来的行为将脱离掌控,这样的 bug 极难定位
  3. 如果a的值作为指针指向堆区,那么此时也恭喜你,代码中动态分配的内存已经被你破坏掉了,那么程序接下来的行为同样脱离掌控,这样的bug也极难定位

----------伟大的分割线-----------

PHP饭米粒(phpfamily) 由一群靠谱的人建立,愿为PHPer带来一些值得细细品味的精神食粮!

饭米粒只发原创或授权发表的文章,不转载网上的文章

所发的文章,均可找到原作者进行沟通。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-02-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 PHP饭米粒 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 错误的理解指针运算
  • 解引用有问题的指针
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档