进入编译器后,一个函数经历了什么?

我是一个函数

我是一个函数,名叫str_upper,我能够把输入的字符串从小写变成大写。不信你看,我长这样:web

charstr_upper(char* str, int len) {
  
  char upper[256];
  
  if (len >= 256 || len <= 0
    return nullptr;

  for (int i = 0; i < len; i++) {
    if (str[i] >= 'a' && str[i] <= 'z') {
      upper[i] = str[i] - 32;
    } else {
      upper[i] = str[i];
    }
  }
  
  return upper;
}

上面是个人源代码形式,听个人好朋友str_lower说,一下子咱们就要一块儿被送到一个叫编译器的地方加工处理了,我内心惧怕极了。编程

编译器之旅

没多久,咱们就来到了这里,一座很庞大到高楼,里面有好多精密的机器在不停的运转着。跨域

一进入大厅,好多函数代码在这里排队等待。数组

我抬头向上望去,不知道有多少层楼,每一层都有一个指示牌,从下往上分别写着:微信

  • 预处理
  • 词法分析
  • 语法分析
  • 语义分析
  • ···

再往上太远就看不太清楚了。app

全部的函数代码按照文件为单位排好队,静静地等待着。编程语言

不过没有等过久,就轮到了咱们这一队。编辑器

来了一个工做人员把咱们带到了一个房间,让咱们都好好躺着,一台机器快速的从头至尾扫描了一遍,将咱们所在文件中出现的#include#define所有给替换掉了。函数

接着,经过房间里的电梯,将咱们送上了二楼。flex

接下来的一段时间,咱们在好几层楼都作了“体检”,每一个函数都被那些像CT同样的机器照了个遍。

不一下子,来到了编译层,这一层有一个特别奇怪的机器,我看到一个个函数被送了进去,出来的时候都变了样子。不只如此,接待处的工做人员看起来很凶,我这下更加紧张了。

函数调用约定

工做人员拿到了个人资料,瞅了几眼,问到:“请问你的调用约定是什么?”

我有些懵,不太懂他的意思,小声问到:“很差意思,你刚问什么?”

工做人员有点不耐烦了,提升了音量,“我是问你调用约定是什么?调用约定啊!”

看见我仍然一脸茫然,工做人员直接给个人资料上调用约定那一栏盖上了一个标记:cdecl

我有点摸不着头脑,同行的小伙伴str_lower拽了我一下说到:“他是在问你函数的调用约定,就是约定调用函数的方式,涉及怎么传递参数,谁来恢复调用栈等”

他这一说我才反映过来,“这个调用约定都有哪些可选的呢?”

“通常有三种:”

  • cdcel,参数从右往左入栈,主调函数负责恢复栈平衡
  • stdcall,参数从右往左入栈,被调函数负责恢复栈平衡
  • fastcall,参数经过寄存器传递,寄存器不够再用栈传递

“他刚才看你没有显式声明,就默认给你cdecl的方式了”,小伙伴继续说到。

我点了点头,原来调用个函数还有这么多讲究呐!

Stack Canary

“别闲聊了,快进去吧!”,工做人员催我了。

我准备走向那台可怕的机器。

“唉,等一下”,正紧张着,工做人员又叫住了我。

我回头看去,工做人员正招手让我过去。

“你好,是个人代码有什么问题吗?”,我紧张的问到,生怕有错误被打回去,连累咱们整个文件都要被遣返。

“不是,是我注意到你的函数里有一个局部数组,须要给你加一下栈溢出保护”,工做人员说到。

我看了下个人代码,确实有一个局部字符数组:

char upper[256];

“栈溢出保护是什么啊?”,我小声问到。

工做人员没有搭理我,忙着给个人资料上加东西。

旁边的小伙伴又把我拽了过去,说到:“我们函数里面定义的局部变量、参数是存放在线程栈里面的。线程要不断游走在不一样的函数中,调用函数后为了能回到原来的地方,调用以前把返回地址也放在了线程栈里。就像这样,你看会不会有什么问题:”

我仔细看了下,“哦,要是越界访问个人upper数组,那就能够修改返回地址,那可就危险了!”

“很聪明嘛!”

“那这个怎么加保护呢?”,我问到。

“你看,函数进来以前,先在局部变量和返回地址之间设置一个数值,函数返回以前再去检查一下,若是栈里的数据被破坏了,检查这个数值就能发现,提早抛出异常!”,小伙伴耐心的解释到。

“这样啊,那岂不是要把我打回去加上你说的这些设置和检查代码?”,我继续提问。

这时,工做人员听到了咱们的闲聊,“不用,咱们编译器自动添加好了,快去吧,已经处理好了”

我瞥了一眼,看到个人资料上增长了一个叫Stack Canary的标记。

我当心翼翼的走进了那架奇怪的机器,马上就失去了知觉,等我醒来时,个人身体已经发生了变化,变成了一堆奇怪的代码,如今我长这样了:

连接

没过一下子,咱们这一队的全部函数代码都编译完成,你们从原来的.c文件都搬到了新家:一个.o文件,我也再次见到了小伙伴str_lower。

“我们是否是已经完成了编译,能够离开这里了吧?”

“还不行,编译虽然是完成了,还差连接这一步呢!”

又过了一小会儿,和咱们一块儿过来的其余文件的函数代码也编译完成了,我们一堆.o文件一块儿被送到了编译器大厦的顶楼:连接层。

这一层也有一个巨大的机器,机器背后链接了一个管道,不知通向了哪里。

咱们这一批的全部.o文件挨个走进了这个巨大的机器,像是一条时空隧道通常,穿行于其间,我感受到了巨大的压力把咱们挤压在了一块儿,很快咱们再一次失去了意识。

醒来以后,我发现全部的函数们都被合在了一个文件中,这是一个可执行文件,而个人身体也再次发生了变化,变成了一段段的二进制指令,如今我长这样了:

终于离开了编译器,真是一趟难忘的旅程,不过我不再想来了······

彩蛋

没想到命运跟我开了一个玩笑,个人第一次运行就出了错!

我又要被打回去从新改造,再走一遍这魔鬼般的旅程。

你能帮我看看,个人代码哪里有错吗?

往期TOP5文章

太慢不能忍!CPU又拿硬盘和网卡开刀了!

由于一个跨域请求,我差点丢了饭碗

完了!CPU一味求快出事儿了!

哈希表哪家强?几大编程语言吵起来了!

一个HTTP数据包的奇幻之旅


本文分享自微信公众号 - 编程技术宇宙(xuanyuancoding)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索