本节中使用的IDE为Kei MDK
1. 汇编、编译和链接
先上一张老图,然后我们仔细看下这个器、那个器的都是干啥的:
你可能会问,这东西了解了又能怎样?有什么用吗?那么好,我们就来看一下了解编译原理是不是真的就那么没用?
对于Keil来说,这几个东西在“keil根目录\ARM\ARMCC\bin”里面,如下图所示:
这几个东东就是keil编译器的内核,他们可以完成从C代码到可以烧写进MCU的二进制件的转换过程。其中
- armar.exe 预处理器
- armcc.exe 编译器
- armasm.exe 汇编器
- armlink.exe 链接器
- fromelf.exe 是elf文件的实用工具集
有了这几个东东,你甚至不用打开keil就能干活,直接在windows的shell里面通过命令行就可以完成编译链接。
当然了你也可以在集成开发环境中见到这几个货:
如上图:
- 汇编器相关选项
- 编译器相关选项
- 预处理器相关选项
- 链接器描述文件(*.map)中包含的内容
还有这个:
上面是专门的链接器相关配置的界面,那么这些东东都是干嘛用的呢?
让我们先来举个栗子,考虑比如下面这样一段代码:
1 | Reset_Handler PROC |
启动代码中的`Reset_Handler`代码,大家可以一眼看出来这段代码是错误的,其中红色字体的`errorfunc`是我故意填在这里的一个不存在的函数,编译必然报错,那么我们看看编译器是怎么报错的?
重点在图中蓝色底纹的部分,注意第一个单词:assembling……,然后后面跟着出错信息,没错,这是一个汇编器错误;再看下面,我再代码里面给出一个错误(我在main.c里面删掉了一个头文件):
还是看蓝色底纹部分,注意第一个单词:compiling……,然后后面跟着出错信息,没错,这是一个编译器错误,跟上面的不同,这是编译器报错,我们再看下面一个错误,代码如下:
1 | int main(void) |
错误的是红色字体的不存在函数`errorfunc`,我们make一下整个工程,看看怎么报错:
还是看蓝色底纹部分,注意第一个单词:linking……,然后后面跟着出错信息,这次是一个链接器错误!
发现了吗?大家经常说的编译器报错,其实是几个不同的东西再报错,编译器、汇编器、链接器都会报错,那你可能会问,知道这个有啥用呢?当然是有用的,比如汇编器报错,基本跟C语言没关系,基本可以断定是汇编语言语法错误或者是嵌入C语言的汇编语言出错(在C中嵌入汇编是一种非常有效的编程手段);如果是链接器报错,那就基本跟C语言语法无关,不是你的C语法上出错,很可能是你调用了不存在的函数或者链接器脚本写错了,或者使用了不存在的标号Symbol;而只有编译器报错,才总是你的C写的有问题。
我见过很多程序猿,看见报错就马上在自己的C代码里面翻来覆去的找,结果根本找不到错误,所以看到错误的第一反应是看看是谁报错?汇编器、编译器、还是链接器,这样可以排除掉N多可能性,可以帮助你迅速排查错误。以上示例代码很简单,大家可以一眼就看到错误,但是如果你的代码十分巨大,内部代码调用关系十分复杂的情况下你就会发现这个技巧相当有效。
2. 你就是老板,当然要学会看工作报表:
我就是个写代码的,不是BOSS,干好活就得了。但是做开发环境的可不这么想,对于IDE来说,他们才是干活的,你是告诉他们怎么干活的老板,他们干了哪些事情,会通过报表的形式汇报给你,这就是编译器生成的中间文件,要当一个好老板当然得会看报表,所以一个合格的写代码的要学会看编译器的中间文件。
看下图……:
这是一个LPC5408的hello world例程由编译器生成的中间文件,这些就是你的“预处理器员工”、“编译器员工”、“汇编器员工”、“链接器员工”的工作报表。我们来一起看一下:
首先是扩展名为“.i”的预处理器文件,这货几乎很少用到,基本就是把C文件中用到的头文件内容导入到C语言中,如果头文件部分有问题,是比较容易找到问题的,很少会需要“.i”的协助,这里就不列出来看了,大家有兴趣自己打开这种文件看一下。
然后是“*.lst”和“*.txt”编译器&汇编器的报表文件,也很少用到,这里略过。
最后是真正有用的链接器描述文件“*.map”非常有用,绝对是你的“核心员工”的工作报表,也是最复杂的一个,工程配置里面可以看到其配置项,非常的多,在工程配置的Listing选项卡里面,如下图:
整个*.map文件也是巨长,里面大概的内容有:
(1)标号表
链接器生成的所有标号,包括所有函数名&全局变量名称都可以生成标号,每一个标号的地址以及其占有的存储空间情况。
(2)存储空间占用情况
这里只列一部分,map文件里面更详细,连每一个函数的占用情况都会列出来。
(3)所有被链接(不是编译)的函数列表及其地址,没有被链接的也有,如果你有兴趣看的话
如下图,截取了一小部分
但这些东西具体有什么用,我们会在以后的文章中大量涉及,这里很难单独说清楚,先打下一个基础,我们以后的文章会经常涉及这个map文件,如果你真的把这个文件看明白了并能熟练应用,那么恭喜你,你就解锁了一个重要技能包。
这里对编译器部分介绍很简单,以后其他文章里面我们会深入探讨,然后我也打算写一篇介绍GCC/GNU工具链用于嵌入式开发的文章,那是一片更广阔的天地。
原本打算写一篇深入介绍编译器原理&应用的文章,但写到这里还是写不下去了,主要是相互依存的点很多,避开这些点单独谈编译器原理&应用是非常不现实的。想来想去,这些体系性的东西还是要循序渐进,以后随着我们解锁了更多的技能包我们再会深入探讨这部分内容。
转自:https://blog.csdn.net/weixin_39118482/article/details/79657178
