打印
[软件资料]

就删个printf,代码崩了!

[复制链接]
719|14
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本文根据真实历史事件改编,如有雷同,可能不是巧合!

请佛祖给代码开个光吧

某年某月某日,晚十点,程序员张三刚垒完一个BUG,长舒一口气,伸了伸懒腰,心中窃喜:这么快就被我搞定了,真是天纵英才!

稍事休息,给女神刷了几朵小红花,看下时间,快11点了,赶紧跑个测试吧!

<p>root@ubuntu:printf# gcc -g test.c -o test</p><p>root@ubuntu:printf# ./test </p><p>Segmentation fault</p>

顿感胸闷,恍惚间,似乎看到一万只神兽在屏幕上奔腾而过!

这可咋整?眼看都要11点了,都快赶不上2路汽车了!

突然灵机一动,要么请佛祖来开个光吧!于是乎,整衣敛容,焚香祷告,遂有了下面这段神奇的代码:


再编译运行一下,居然正常运行,Segmentation fault消失了!



大喜!遂提交代码,起身,出门,奔向2路汽车...



一夜无语。




使用特权

评论回复
沙发
小夏天的大西瓜|  楼主 | 2023-12-16 11:11 | 只看该作者
      李大仙的高光时刻
第二天,张三和平时一样,手里拿着鸡蛋饼,脚上提拉着人字拖,刚踩着点踏入办公室,明显感觉氛围与往日不同,大家看自己眼神都有一种异样,对,那是发自内心的钦佩与敬畏!

故作淡定,来到自己工位上,刚把电脑打开,旁边美女同事便凑过来,悄声问:“张大仙,你是怎么做到的啊?太厉害了!”

原来,早上有人看到了张三昨晚提交的代码,很是震惊!有点不信邪,非要把那段开光的代码给删掉验证一下,结果可想而知,程序崩了!于是,再把开光代码加上,居然神奇的又好了!那人差点被惊掉下巴!于是,一传十,十传百,整个公司都知道了这个神话!

此时,张三身边已经围了几十个人,纷纷要求大师给传授一下佛法。张大仙清了清嗓子,刚要开口,却听见人群外传来一声:“故弄玄虚,装神弄鬼!”

说此话的,正是公司的技术大拿:尼古拉斯.李四。

尼古拉斯.李四的挑战
“谁装神弄鬼了,不要乱说!”张三不服!

“哼!这么明显的问题,还要搞个什么佛祖开光,真是笑死人了!”李四不屑地说。

“赵大拿,既然如此,来给兄弟们讲讲呗!”, 有人起哄。

“哥渴了,给哥倒杯水去!”

“好嘞!”,那人屁颠屁颠的去了。

李四,喝一口水,开始娓(继)娓(续)道(装)来(B)。

使用特权

评论回复
板凳
小夏天的大西瓜|  楼主 | 2023-12-16 11:17 | 只看该作者
      没开光的代码为什么会挂
“首先,咱们来看看,没有开过光的代码为什么会挂!”,李四打开VS Code,把那段开光代码给删掉了:



“我知道了”,小明突然喊道,“bar()中的局部变量idx没初始化,是一个随机值,所以buf[idx] = 'A'这一句内存访问异常了!”。

“这么简单的问题,谁都看得出来!”,李四瞥了小明一眼,继续说道:“问题是,idx真的是一个随机值吗?如果是随机值的话,理论上讲,这个程序应该还是有概率能成功运行的,对吧?可现在为什么会100%每次都segfault呢?”

“请大神赐教!”,小明有点不好意思了。

“咱们先在GDB中看一下idx的值是多少”,李四打开GDB,开始调试:



“idx = 16843009,这看起来不就是个随机值嘛!”,小明有点不服气。
“随机值?你再看!”,李四在GDB中敲了一条命令:

“呃,原来idx = 0x01010101,我明白了”,小明恍然大悟,“这个是在fool()中memset(array, 1, sizeof(array))这一句残留下来的值!”
“是的,说到点子上了”,李四表示赞许,“C语言中未初始化的局部变量,并不是真的随机值,而是当时栈上的残留值,这个值有可能是之前某个函数执行过后残留在栈上的,只不过我们一般不太关心栈上的残留值,所以看起来像是个随机值”,李四继续说,“我们看下fool()中array的地址和bar()中idx的地址就明白了”。
一边说着,李四又在GDB中重新运行程序,并且在第8行和第13行分别设置了一个断点,把array和idx的地址打印了出来:
“看到了吧,array[9]的地址和idx的地址一模一样!”
“原来如此!”,众人恍然大悟!


使用特权

评论回复
地板
小夏天的大西瓜|  楼主 | 2023-12-16 11:20 | 只看该作者
      开了光的代码为什么正常运行
“那为什么开了光之后,程序就又100%运行正常了呢?”,小明还是不解。

“咱们张大仙牛X呗!”,李四有些戏虐地看着张三。

“别整这些没用的,你倒是说说看啊!”,张三有些脸红了。

“So,easy!”,李四有些得意,“佛祖开了光之后,bar()中的idx的值变了呗!”

“我不信!”,张三还在嘴硬!

“Okay,且看哥如何收拾你这妖道”,李四摆出一个降妖伏魔的姿势。

“哈哈哈,张大仙,还不赶紧跑,小心被李天师抓了祭天呀!”,有人起哄。

张三瞪了那人一眼,没有说话。

“李天师,别得瑟了,赶紧拿出真本事,降妖除魔啊!”,那人继续起哄!

“小case,且看哥来祭出法宝,收了那妖道!”,李四有些得意。于是,李四又把佛祖开光的那段代码给它加了回来:


然后,重新编译,在GDB中调试:



“idx = 0, 果然是被printf给改了!”,众人佩服不已,对李四竖起了大拇指!

使用特权

评论回复
5
小夏天的大西瓜|  楼主 | 2023-12-16 11:22 | 只看该作者
      李天师自己开光 - 翻车了!
“怎么样?服不服?”,李四此时那是相当得意啊!看这张三满脸通红,又得瑟道:“哥不需要请佛祖开光,哥自己就能给这段代码开光,你信不信?”

“我不信!你不许给bar()中的idx赋初值!”,张三不服!

“那当然!”,李四很是自信,又在VSCode里把代码简单修改了一下:



“接下来,是见证奇迹的时刻了!”,尼古拉斯.李四很是自信!重新编译,运行:



“哈哈!翻车了吧!让你得瑟!”,看到程序还是Segmentation fault,张三感觉自己扳回一局!

“是啊,李天师,你这法术不灵啊,道行可不如张大仙啊!哈哈哈”,众人打趣。

“WTF,翻车了!”,李四心里一惊!

李四有点不解,按照自己的理解,那个所谓佛祖开光的代码,肯定是因为printf执行了之后,把fool()残留在栈里的值给改了,idx的值变了,所以bar()里才不会触发内存访问异常了。可是,为什么现在替换成printf("Hello, World!"),就不行了呢?那么现在idx的值又被改成多少了呢?还是先用GDB看下吧:



“呃,原来idx的值还是fool()里的残留值,李天师,看来您这还是得继续修炼啊,开光失败了!哈哈哈!”,众人起哄道。

李四现在也是面红耳赤,突然,**一闪!“哥知道了!”,李四大叫一声!

使用特权

评论回复
6
小夏天的大西瓜|  楼主 | 2023-12-16 11:24 | 只看该作者
本帖最后由 小夏天的大西瓜 于 2023-12-16 11:27 编辑

李天师二次开光
于是乎,再打开VSCode,简单修改下代码:
重新编译运行:
居然真的成功了!
“看来**还是有两把刷子的!”,众人称赞道!
“不过,你只是在原来打印Hello, World的基础上加了个换行符‘\n’,为什么就成功了呢?”,众人不解。
只见李四露出稍显猥琐的笑容,故作神秘:“想知道啊?”
“想!”,众人一致回答。
“哥,不!告!诉!你!”
“呸!”,一人一口吐沫朝李四喷来!
尼古拉斯.李四,遂溺水,卒!

使用特权

评论回复
7
小夏天的大西瓜|  楼主 | 2023-12-16 11:25 | 只看该作者
     正经的后记
之前写了几篇关于程序调试的文章,很多童鞋觉得还不错,于是经常收到一些小伙伴私信问一些调试相关的问题。其中好几个小伙伴都提到过这种问题,就是原本很容易出现的问题,加了句printf或其他log之后,问题再也不出现了,导致很难定位。

这类问题,根据我的经验,99%的情况下,printf是不能真正解决问题的,只是把问题隐藏起来了,比如文中这种典型的栈相关的问题。还有比较常见的是多线程时候,加了printf,很有可能会引起线程间的时序逻辑的变化,因为printf是用write()系统调用实现的,而系统调用又是Linux上非常重要的调度点之一,所以很容易触发线程调度等,改变线程时序逻辑。

而另外1%的情况,printf(或printk)或其他log方式,确实是可以解决问题的。比如涉及到内存屏障、指令乱序等相关问题,或者比较典型的是在一些硬件相关的程序中,如bootloader、驱动等。这类问题比较少见,不再赘述。

总之,遇到这类问题,还是要先仔细分析下,printf(或其他log方式)可能会对程序逻辑产生什么样的影响,然后再对症下药!

使用特权

评论回复
8
jf101| | 2023-12-19 09:56 | 只看该作者
楼主的这个小故事,对于程序编写确实很重要,细挖问题真因

使用特权

评论回复
9
AdaMaYun| | 2023-12-19 09:56 | 只看该作者
rintf(或printk)或其他log方式,确实是可以解决问题的

使用特权

评论回复
10
LOVEEVER| | 2023-12-19 10:05 | 只看该作者
楼主这个故事真的蕴含无限的哲理,值得细品

使用特权

评论回复
11
OKAKAKO| | 2023-12-19 10:11 | 只看该作者
加了句printf或其他log之后,问题再也不出现了,导致很难定位!

使用特权

评论回复
12
小小蚂蚁举千斤| | 2023-12-19 10:43 | 只看该作者
printf打印确实有时需要底层对硬件了解一些

使用特权

评论回复
13
中国龙芯CDX| | 2023-12-21 09:03 | 只看该作者
楼主这个故事挺有意思,针对问题还得深挖问题本质

使用特权

评论回复
14
星辰大海不退缩| | 2023-12-21 12:36 | 只看该作者
printf打印确实有时需要底层对硬件了解一些

使用特权

评论回复
15
szt1993| | 2023-12-22 08:25 | 只看该作者
就删个printf,代码崩了,这个确实是对于内存细节把握很重要

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

199

主题

1668

帖子

2

粉丝