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

来源 | 编程技术宇宙

责编 | Carol

我是一个函数

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

char* str_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说,一会儿我们就要一起被送到一个叫编译器的地方加工处理了,我心里害怕极了。

编译器之旅

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

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

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

  • 预处理

  • 词法分析

  • 语法分析

  • 语义分析

  • ···

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

所有的函数代码按照文件为单位排好队,静静地等待着。

不过没有等太久,就轮到了我们这一队。

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

接着,通过房间里的电梯,将我们送上了二楼。

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

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

函数调用约定

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

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

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

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

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

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

“一般有三种:”

  • cdcel,参数从右往左入栈,主调函数负责恢复栈平衡

  • stdcall,参数从右往左入栈,被调函数负责恢复栈平衡

  • fastcall,参数通过寄存器传递,寄存器不够再用栈传递

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

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

Stack Canary

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

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

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

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

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

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

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

char upper[256];

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

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

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

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

“很聪明嘛!”

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

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

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

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

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

我小心翼翼的走进了那架奇怪的机器,立刻就失去了知觉,等我醒来时,我的身体已经发生了变化,变成了一堆奇怪的代码,现在我长这样了:

链接

没过一会儿,我们这一队的所有函数代码都编译完成,大家从原来的.c文件都搬到了新家:一个.o文件,我也再次见到了小伙伴str_lower。

“咱们是不是已经完成了编译,可以离开这里了吧?”

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

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

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

我们这一批的所有.o文件挨个走进了这个巨大的机器,像是一条时空隧道一般,穿行于其间,我感觉到了巨大的压力把我们挤压在了一起,很快我们再一次失去了意识。

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

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

彩蛋

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

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

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


热门文章

暂无图片
编程学习 ·

《伸手系列》之分布式锁Redssion入门和源码解析

Redisson简介 Javaer都知道Jedis,Jedis是Redis的Java实现的客户端,其API提供了比较全面的Redis命令的支持。Redission也是Redis的客户端,相比于Jedis功能简单。Jedis简单使用阻塞的I/O和redis交互,Redission通过Netty支持非阻塞I/O。Jedis最新版本2.9.0是2016年的快3年了没…
暂无图片
编程学习 ·

ASP.NET Core2.0项目实战-003

Views布局(Layouts)视图组件(ViewComponents)分部视图(PartialViews)HTML助手(HtmlHelpers) 页面标签的辅助类Tag助手(TagHelpers)配置全部视图(Global view configutation)视图找的时候如果home里面没有还会找shared文件夹里面的using System; using System.Colle…
暂无图片
编程学习 ·

Leetcode 题解 - 字符串

字符串循环移位包含 编程之美 3.1 s1 = AABCD, s2 = CDAA Return : true给定两个字符串 s1 和 s2,要求判定 s2 是否能够被 s1 做循环移位得到的字符串包含。 s1 进行循环移位的结果是 s1s1 的子字符串,因此只要判断 s2 是否是 s1s1 的子字符串即可。 字符串循环移位 编程之美…
暂无图片
编程学习 ·

无线传输技术有哪些?

什么是ZigBee? ZigBee,也称紫蜂,是一种低速短距离传输的无线网上协议,底层是采用IEEE802.15.4标准规范的媒体访问层与物理层。主要特色有低速、低耗电、低成本、支持大量网上节点、支持多种网上拓扑、低复杂度、快速、可靠、安全。 三大特征、八大优势 特征一 ZigBee能源消…
暂无图片
编程学习 ·

记一次spark-submi 提交python脚本 遇到的问题

一、通过spark-submit 提交报错如下 yarn运行模式spark用的版本是2.4.0是支持pandas_udf的,而且通过pyspark的shell命令行一条条执行 都是没有问题的 但是将代码作为文件用spark submit提交就报这个错误 二、解决办法: @pandas_udf(returnType=“string”, PandasUDFType.…
暂无图片
编程学习 ·

自增自减及逗号表达式

** 自增自减及逗号表达式 **一.自增、自减运算符1.自增运算符++和自减运算符–都是单目运算符,功能是使变量的值加1或减1。 2.自加或自减只能用于变量,而不能用于常量或表达式。 3.用于++、–运算的变量只能是整型、字符型和指针型变量运行下面的程序,观察并分析运算规则#in…
暂无图片
编程学习 ·

挂牌一年,关于 5G 的 9 个变化

简介:2019 年 6 月 6 日的一张新闻图片瞬间刷遍全网,意味着中国正式进入 5G 时代,2019 年也被业界称为 5G 商用元年。转眼间一年过去,这个不断被提及的 5G 新星发展到了什么程度呢,让我们再来回顾和展望一下。3GPP 标准进展移动通讯网络作为全球的基础设施,标准化是基础,…
暂无图片
编程学习 ·

latex小技巧

latex小技巧 大学写论文需要用到的latex编辑工具,当初从0开始学习着实走了很多弯路,现在大学毕业了,估计这个东西很长一段时间不会用到了,做个笔记以后遗忘了可以来回看一看。 在已经具有完整latex模板的前提下,使用下面的内容将帮助你快速完成Latex的编写。 工具栏实用工…
暂无图片
编程学习 ·

IT系统稳定性创新者:分布式软件,“笨马”先跑

(PerfMa CEO 李嘉鹏)早在2006年前后,IT系统稳定性就成为了当时集中式架构的挑战。随着互联网的快速兴起,当时的“Unix+小型机”架构遭遇了数据爆增的冲击。特别是在线交易、商业分析和数据库等关键业务系统,在2010年前后进入了TB甚至PB级,导致传统IT架构不堪重负,对IT系统…
暂无图片
编程学习 ·

Java设计模式-单例模式(全例记录)

1. 基础介绍核心作用:保证一个类只有一个实例,并提供一个访问该实例的全局访问点; 优点:由于单例模式只生成一个实例,所以减少了系统的开销,当一个系统启动需要较多的资源时,可以直接在系统启动时产生一个单例对象,然后使其永久驻留内存;单例模式可以在系统设置全局访…
暂无图片
编程学习 ·

综合练习

一、端午节的淘宝粽子交易 import pandas as pd import numpy as npdf1 = pd.read_csv(zongzi.csv) df1.head()标题价格付款人数店铺发货地址0五芳斋粽子礼盒 心悦+18只装咸鸭蛋组合端午节礼品团购嘉兴肉粽子1296人付款五芳斋官方旗舰店浙江 嘉兴1北京稻香村端午粽子手工豆沙粽…
暂无图片
编程学习 ·

Python中%r和%s的相同点和不同点

1、在处理布尔型或者数字型时,二者是没有区别的 (1)数字型 I am %r years old%22#%r ‘I am 22 years old’ I am %s years old%22#%s‘I am 22 years old’ This building is %r m tall%22.35#%r‘This building is 22.35 m tall’ This building is %s m tall%22.35#%s‘Thi…
暂无图片
编程学习 ·

基于jupyter notebook的python编程-----Win10通过OpenCv-3.4.1进行人脸口罩数据集的模型训练并进行戴口罩识别检测

基于jupyter notebook的python编程-----Win10通过OpenCv-3.4.1进行人脸口罩数据集的模型训练并进行戴口罩识别检测目录一、OpenCv的下载及安装1、OpenCv的下载2、OpenCv的安装3、查看是否具有模型训练环境二、人脸口罩数据集的下载及处理1、人脸口罩数据集下载2、数据集重命名为…
暂无图片
编程学习 ·

nat表中的dnat snat的使用(iptables)

nat表中的dnat snat的使用- snatiptables -t nat -A POSTROUTING -o enp6s0 -j SNAT --to-source 172.25.254.33- dnatiptables -t nat -A PREROUTING -i enp6s0 -j DNAT --to-dest 170.25.254.22从enp6s0进来的所有数据都转给170.25.254.11,即给enp6s0(172.25.254.33)的所有…
暂无图片
编程学习 ·

springboot静态资源无法加载最后原因竟然是拦截器没有生效

今天打开项目,发现页面的样式没了,用F12调试,果不其然,css,js等样式没有加载成功 后面看控制台发现报 No mapping for GET 静态资源。 很明显,访问静态资源的请求被springboot拦截了。 然后我又看自己写的拦截器 @Overridepublic void addInterceptors(InterceptorRegist…
暂无图片
编程学习 ·

LeetCode刷题之动态规划的解题方法及相关练习

从集合的角度来考虑DP问题,用某一个数来代表一类数线性DP: 快乐的LeetCode — 53.最大子序和快乐的LeetCode — 120. 三角形最小路径和多口味LeetCode — 63. 不同路径 II多味的LeetCode — 91. 解码方法区间DP问题 多味的LeetCode — 198. 打家劫舍多味的LeetCode — 300. 最…
暂无图片
编程学习 ·

随笔 弹窗 二维码生成及图片下载

一、qrcode-vue模块该模块是用来动态生成二维码的vue模块插件,<qrcode-vue></qrcode-vue>的底层其实是一个<canvas></canvas>标签。要想使用qrcode.vue插件,需要用vue的脚手架安装这个插件安装指令npm install qrcode --save-dev,在这里我举一个例子…
暂无图片
编程学习 ·

nginxj简单安装文档

安装依赖包yum -y install pcre-develyum -y install openssl-develyum -y install gccyum -y install lrzszyum -y install openssh-clients安装nginx上传su - root1、cd /usr/local2、rz –y解压tar -xzvf nginx-1.7.7.tar.gz重命名mv nginx-1.7.7 nginx安装nginx进入解压后的…
暂无图片
编程学习 ·

&& 、 ||和!! 运算符分别能做什么?

很明显这几个都是逻辑运算符!基本用法我就不说了,自己去看文档,先看几个简单的例子吧! var a = null,b = 10; if (b >= 10) {a = 1; } else if (b >= 20) {a = 2; } else if (b >= 30) {a = 3; } else if (b >= 40) {a = 4; };这样写是不是很难看,有些人还会用 …