Hello World历程
Hello World,很简单的一句话,很多程序员入门新语言的第一行代码,能输出这句话在屏幕,说明整个开发环境都已经搭建好了,可以进一步开发了,如果连这句话都无法输出,后面做再多算法设计也没用,因为,你的软件无法在客户机使用,那么,计算机在输出Hello World的背后做了什么呢,下面记录一下。
且看最简单的C++代码:
在Linux下使用G++编译器编译运用如下:
在生成可执行文件a.out背后,经历了如下几个过程:
预处理(PrePressing) -> 编译(Compilation) -> 汇编(Assembly) -> 链接(Linking)
A. 预处理
主要处理源码文件中以"#"符号开头的预编译指令,如“#include”、"#define"等。预处理过程中,大致做了如下几件事情:
1. 展开宏定义
2. 处理条件预编译指令,如"#if"
3. 处理文件包含预编译指令
4. 删除所有注释
5. 添加行号和文件名标识,便于编译时编译器产生调试需要用到的符号信息
6. 保留#pragma编译指令,该指令主要编译器会用到
预编译如下:
-E参数是进行预编译,生成的文件的扩展名为*.i 。调试时,如果觉得宏定义或文件保护不对,可以看看这么预编译文件是否正确。
B. 编译
在预处理的基础上,对预处理生成的文件进行词法分析、语法及语义分析及优化,产生汇编代码文件。如下:
-S参数是进行编译。
C. 汇编
对编译生成的汇编代码文件进行翻译生成机器码文件。如下:
-c参数是进行汇编。也可以一步到位:g++ -c main.cpp -o main.o。
可以用命令"objdump -s -d main.o"来看它的格式,如下(objdump属于binutils工具集的其中一个工具):
可以看到,目标文件按段来存储的,如一般编译后的执行语存放在.text段,已初始化的全局变量和局部静态变量存放在.data段,未初始化的全局变量和局部静态变量存放在.bss段(用于定义符号并且为符号预留给定数量的未初始化空间)。上面输出信息比较详细,如果需要总览每个段的信息,使用"-h"参数即可:
这里可以看到目标文件格式通用的格式,包含拥有的数据段及其权限、作用(.note.GNU-stack为堆栈提示段, CONTENTS:表示该段在文件中存在)。
D. 链接
前面只是针对一个文件生成目标机器码文件,要运行起来,还需要很多其他依赖的目标文件,类似与操作系统启动,操作系统是安装在硬盘的,直接启动是不行的,需要配合硬盘BIOS的那512字节,逐段加载到内存,经历一系列初始化,最终进入保护模式等,同样,要启动应用软件a.out,也是需要很多依赖,拼装成一个可执行软件,才可以运行。
链接过程主要有包括了地址和控件分配、符号决议和重定位等。
g++ main.o -o main
可以用ldd命令看其具体的依赖库: