c++从源文件到可执行文件的步骤详解

编译与链接有四个过程:

1)预处理

2)编译

3)汇编

4)链接

 

 

(1)预处理

源文件和头文件被预处理成一个.i文件、(-E表示只进行预处理)

g++ -E hello.cpp -o hello.i

-E:意味着只执行到预编译,直接输出预编译结果。

预处理过程主要处理那些源文件中的以“#”开始的预编译指令。包括#include,#define, #if,等等。

主要的处理规则如下:

(1)将所有的#define删除,并且展开所有的宏。

如#define a b  就是将所有的a替换成b。但作为字符串常量a则不替换。

(2)处理所有的条件预编译指令,,如#if,#ifdef,#else,#endif(以此来决定对哪些代码进行处理,将那些不必要的

代码过滤掉)

(3)处理#include预编译指令,将被包含的文件插入到该预编译指令的位置。()这个过程是递归进行的。其中系统提供的头文件一般放在/usr/include下面,用<>表示。开发人员自定义的头文件放在与源程序同一个目录下,用“”表示。

(4)过滤所有的注释“//“和”/*  */“之间的内容。

(5)添加行号和文件名标识。比如 #2  "test.c" 2

(6)保留所有的#pragma编译器指令,因为编译器需要使用他们。(下面是pragma的一些参数,详情看 https://baike.baidu.com/item/%23pragma

 

 

(2) 编译,汇编

编译过程就是把预处理的文件进行一系列的词法分析语法分析语义分析以及优化后产生相应的汇编代

码文件。相当于:

g++ -S hello.i -o hello.s

-S(大写):表示只执行到源代码到汇编代码的转换,输出汇编代码。

编译器就是将高级语言翻译成机器语言的一个工具。

编译过程分为6步:

词法分析(扫描)

语法分析

语义分析

源代码优化

(其实应该上面四个才叫编译)

(下面两个叫汇编了)

代码生成

目标代码优化

 

由一个例子来分析:

array[index]=(index+5)*(2+7)

 

1)词法分析(扫描)

运用类似于有限状态机的算法将源代码的字符分割成一系列的记号。如上面的,一共包含28个非空字符

串,产生了16个记号。

词法分析产生的记号一般分为几种:

关键字

标识符

字面量(数字,字符串等)

特殊记号(加号,等号等)

另外,扫描器也完成其他一些工作,比如将标识符存放到符号表中,将数字,字符串常量存放到文字

词法分析工具(lex)

 

2)语法分析

将由扫描器产生的记号进行语法分析,从而产生语法树。

语法树:以表达式为结点的树。(c语言中,一个语句就是一个表达式)

 

另外,在语法分析时,很多运算符的优先级和含义也被确定下来。

语法分析工具(yacc)

 

3)语义分析

就是看看这个语句是否有意义。比如两个指针相乘,这是没有意义的,不过在语法分析的时候是合法的。

编译器能分析的语义是静态语义

静态语义:在编译期间可以确实能的语义

动态语义:在运行期间才能确定的语义,比如将0作为除数是一个运行期语义错误。

静态语义通常包括声明类型的匹配以及类型的转换

如果有写了类型转换需要做隐式转化,语义分析程序会子啊语法中插入相应的转换节点。

语义分析也对符号表里面的符号做了更新

 

4)中间语言的生成

源代码优化器会在源代码级别进行优化。上面那个例子中,(2+7)被优化成9。

中间代码一般跟目标机器和运行时环境是无关的,比如不包含数据的尺寸,变量的地址和寄存器的名字等

等。

中间代码使得编译器可以被分为前端和后端。

前端负责产生机器无关的中间代码

后端负责将中间代码转换为目标机器代码

这样,对于一个跨平台的编译器而言,可以针对不同的平台使用同一个前端和针对不同的机器平台的后端

个数

 

 

5)目标代码的生成与优化。(这两个其实就是汇编)

编译器后端包含:

代码生成器(汇编):将中间代码转换成目标机器代码,这个代码十分依赖于目标机器,因为不同的机器有着

不同的字长,寄存器,整数数据类型和浮点数数据类型等。

目标代码优化器:对上面的代码进行优化,选择合适的寻址方式,使用位移来代替乘法等。

 

对于index和array,如果他们定义在和源代码同一个编译单元里面,那么编译器可以为他们分配空

间,确定他们的地址。如果他们定义只其他模块里面,(定义在其他模块的全局变量和函数在最终运行时的绝对地址都要在最终链接的时候才能确定

 

(3)链接

从原理上来说,是把一些指令对其他符号地址的引用加以修正。

 

链接过程主要包括:

地址和空间分配

符号决议

重定位

 

库就是一组目标文件的包,就是一些常用的代码编译成目标文件以后打包存放。

 

重定位:就是一开始编译器在不知道变量的目标地址的情况下,先将目标地址设为0,链接以后再将这个地址修改为它真正的目标地址。这个地址修正的过程就叫做重定位。

 

每个目标文件除了拥有自己的数据和二进制代码以外,还提供三个表:

1)未解决符号表:提供了所有在该编译单元引用但是定义不是在本编译单元的符号以及其出现

地址。

2)导出符号表:提供了本编译单元具有定义,并且愿意提供给其他单元使用的符号及其地址。

3)地址重定向表:提供了本编译单元对所有对自身地址的引用的记录。

编译器将extern声明的变量置入未解决符号表,而不置入导出符号表。这属于外部链接。

编译器将static声明的全局变量不置入未解决符号表,也不置入导出符号表,因此其他单元无法使

用,这属于内部链接。

普通变量及其函数被置入导出符号表。

 

 

链接包含静态链接和动态链接。

1)静态链接。

对函数库的链接是放在编译时期完成的是静态链接。这些函数库被称为静态库,通常为”libXXX.a“形

式。

 

如有5个文件:add.h,add.cpp,sub.h,sub.cpp,main.cpp

先将add.cpp,sub.cpp编译成.o文件

g++ -c add.cpp

g++ -c sub.cpp

无论是静态库文件还是动态库文件,都是由“.o”文件创建的。

由.o文件创建静态库(.a)文件,执行命令:

  ar cr libmymath.a sub.o add.o

这样就会生成libmymath.a文件。其中lib是库文件的开头命名规范,mymyth是库名字,".a"说明是静态

库。

 

在后面指定了 -lmymath。这样g++会在静态库名前加上前缀lib,然后追加扩展名.a得到的静态库文件名

来查找静态库文件。

 

2)动态链接

 

动态链接用如下命令:

 

 

 

g++ -o main main.cpp -L. -lmypath   (注意大写的L后面还有个“.”,表示当前目录)。

上面的链接是正常的,但是执行的时候回出错。

动态库搜索路径为;

(1)编译目标代码时指定的动态库搜索路径

(2)环境变量LD_LIBRARY_PATH指定的动态库搜索路径

(3)配置文件/etc/ld.so.conf中指定的动态库搜索路径;即只需在该文件中追加一行库所在的完整

路径如“root/test/conf/lib”即可;然后ldconfig是修改生效

(4)默认的动态库搜索路径/lib.

(5)默认的动态库搜索路径/usr/lib。

 

   

 

 

3)动态库与静态库重名问题

当静态库文件和动态库文件同名的时候,编译器会先到path目录下搜索libXXX.so(动态库文件),如果没

有找到,这继续搜索libXXX.a(静态库文件);(先找动态库文件,若没找到,再找静态库文件

 

 

4)静态库链接,动态库链接各自的特点

1)动态库链接有利于进程间资源共享。

 

2)将一些程序升级变得简单。

 

3)甚至可以真正做到链接载入完全由程序员在程序代码中控制

 

4)静态库速度更快一些。

 

  • 15
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 使用CMake可以编译具有不同目录和多个源文件可执行文件。 首先,我们需要创建一个CMakeLists.txt文件来描述项目的结构和编译选项。以下是一个示例CMakeLists.txt文件: ```cmake cmake_minimum_required(VERSION 3.10) project(MyProject) # 设置编译选项 set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) # 添加可执行文件目标 add_executable(MyExecutable src/main.cpp src/util.cpp ) # 添加头文件路径 target_include_directories(MyExecutable PUBLIC include ) # 添加可执行文件所需的其他库 target_link_libraries(MyExecutable PRIVATE MyLibrary ) ``` 在这个示例中,我们将项目命名为“MyProject”并设置了CMake的最低版本要求。然后,我们将编译选项设置为C++11,并添加了两个源文件(main.cpp和util.cpp)来创建一个可执行文件“MyExecutable”。还通过“target_include_directories”指定了头文件路径,以及通过“target_link_libraries”指定了可执行文件所需的其他库(例如MyLibrary)。 接下来,我们将在项目的根目录中创建一个“build”文件夹,并在该文件夹中使用CMake来生成编译结果。使用以下命令: ```shell cd build cmake .. make ``` 这将生成一个名为“MyExecutable”的可执行文件,位于“build”文件夹中。您可以通过执行以下命令来运行它: ```shell ./MyExecutable ``` 这样,您就可以通过CMakeLists.txt文件来编译带有不同目录和多个源文件可执行文件。 ### 回答2: 使用C语言编写的项目通常需要一个主目录,作为整个项目的根目录。在主目录下,我们可以将源代码和其他项目文件组织在不同的子目录中。 为了编译一个包含不同目录中多个源文件的C程序,我们可以使用CMake来管理项目的构建过程。CMake是一个跨平台的构建工具,可以根据CMakeLists.txt文件配置项目的构建。 首先,我们需要在主目录下创建一个CMakeLists.txt文件。这个文件将告诉CMake如何构建我们的项目。 CMakeLists.txt文件通常包含以下内容: 1. 设置项目的名称和最低要求的CMake版本: ```cmake cmake_minimum_required(VERSION 3.10) project(my_project) ``` 2. 添加可执行文件,并指定源文件的路径: ```cmake add_executable(my_executable src/main.c src/util.c ) ``` 上面的例子中,我们在src目录下有两个源文件:main.c和util.c。通过将它们包含在add_executable命令中,CMake将会构建一个名为my_executable的可执行文件。 3. 添加其他的源文件和目录: ```cmake add_library(my_library src/other.c src/helpers/helper1.c src/helpers/helper2.c ) ``` 在这个例子中,我们添加了一个名为my_library的库,并指定了多个源文件和目录。 4. 指定链接的库: ```cmake target_link_libraries(my_executable my_library) ``` 上面的命令将my_executable与my_library进行链接。 5. 编译项目: ```bash mkdir build cd build cmake .. make ``` 以上命令将创建一个名为build的目录,在这个目录下运行cmake和make命令来构建项目。 通过上述步骤,我们可以使用CMake和CMakeLists.txt文件来编译包含不同目录中多个源文件的C程序,并生成可执行文件my_executable。 ### 回答3: 编译可以使用cmakelists.txt文件来指导。CMake是一个跨平台的编译工具,可以帮助我们简化编译过程。 在CMakeLists.txt文件中,我们需要包含一些基本内容来指示CMake如何构建我们的项目。首先,我们需要指定我们的最低CMake版本。接下来,我们可以定义项目的名称和版本号。 接下来,我们需要使用project()命令来指定项目的名称和版本号。然后,我们可以定义目标可执行文件的名称,并指定源文件。 如果我们有多个源文件,可以使用add_executable()命令来指定所有源文件的名称。如果源文件位于不同的目录中,我们可以使用相对路径或绝对路径来指定它们的位置。 一旦我们定义了所有的源文件,我们可以使用target_link_libraries()命令来指定与我们的项目相关的库。 最后,我们可以使用add_custom_target()命令来定义自定义目标,以便在构建过程中执行一些自定义命令。 完成了CMakeLists.txt文件的编写后,我们可以使用cmake命令来生成一个Makefile。然后,我们可以使用make命令来编译我们的项目,生成可执行文件。 总结起来,使用CMake进行编译可以帮助我们简化编译过程,并且可以支持多个源文件和不同目录的编译。只需在CMakeLists.txt文件中配置好项目信息、源文件和目标可执行文件的相关命令,然后使用cmake和make命令来生成和编译项目,最终生成可执行文件

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值