【一天一个C++小知识】011.C++编译的流程

本文详细介绍了C语言从源代码到可执行文件的整个编译过程,包括预处理、编译、汇编和链接四个阶段,并对比了静态编译与动态编译的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 过程
1.1 预处理

  也称为预编译,做些文本替换工作。编译器执行预处理指令(以#开头,例如#include),这个过程会得到不包含#指令的.i文件。这个过程会拷贝#include 包含的文件代码,进行#define 宏定义的替换 ,处理条件编译指令 (#ifndef #ifdef #endif)等,#pragma once不会去掉。

  命令:

gcc -E -I./inc test.c -o test.i

  or

cpp test.c -I./inc -o test.i

#pragma once和#ifdef的区别

  都是为了避免同一个文件被include多次,在能够支持这两种方式的编译器上,二者并没有太大的区别,但是两者仍然还是有一些细微的区别,举例如下:

// Test1.h
   #if !define (__TESTONE_H_)
   #define __TESTONE_H_
   #endif
  
// Test2.h
    #pragma once 
         
 // Test.cpp
    #include "Test1.h"      // line 1
    #include "Test1.h"      // line 2
    #include "Test2.h"      // line 3
    #include "Test2.h"      // line 4

  头文件Test1.h中用宏来避免重复,头文件Test2.h中用#pragma once来避免重复。编译Test.cpp,将需要打开Test1.h两次,第一次发现宏__TESTONE_H_没有定义,接着就处理宏定义;第二次打 开Test1.h时,发现宏__TESTONE_H_已经定义过了,编译器就会略过宏定义部分,知道处理完Test1.h末尾的#endif。

  而由于头文件Test2.h使用#pragma once来避免重复定义的,在编译Test.cpp的过程中,Test2.h只会被打开一次,也就是处理到第3行的时候。因为Test2.h用的 是#pragma once,所以在处理完第3行后,编译器已经知道包含了一次Test2.h,在它(编译器)处理第4行代码时,发现Test2.h已经包含过了,忽略掉第 4行代码,也就不需要再次打开Test2.h进行判断了。

  总结一下,除了#pragma once是微软编译器所特有的之外,用宏和#pragma once的办法来避免重复包含头文件,主要区别在于宏处理的方法会多次打开同一头文件,而#pragma once则不会重复打开,从而#pragma once能够更快速,但#ifndef方式由语言支持所以移植性好。

1.2 编译

  通过预编译输出的.i文件中,只有常量:数字、字符串、变量的定义,c语言的关键字:main、if、else、for、while等。这阶段要做的工作主要是,通过语法分析和词法分析,确定所有指令是否符合规则,之后翻译成汇编代码。这个过程将.i文件转化位.s文件(两个都是文本文件)。编译的指定如下:

gcc -S -I./inc test.i -o test.s

静态编译和动态编译

  静态编译:编译器在编译可执行文件时,把需要用到的对应动态链接库(.so或.ilb)中的部分提取出来,链接到可执行文件中去,使可执行文件在运行时不需要依赖于动态链接库。静态lib将导出声明和实现都放在lib中。编译后所有代码都嵌入到宿主程序。

  动态编译: 动态编译的可执行文件需要附带一个的动态链接库,在执行时,需要调用其对应动态链接库中的命令。所以其优点一 方面是缩小了执行文件本身的体积,另一方面是加快了编译速度,节省了系统资源。缺点一是哪怕是很简单的程序,只用到了链接库中的一两条命令,也需要附带一个相对庞大的链接库;二是如果其他计算机上没有安装对应的运行库,则用动态编译的可执行文件就不能运行。

  动态链接库:创建一个动态链接库,会生成x.dll,x.lib。动态链接库有两种加载方式:1.一种是静态加载,就是在编译的时候就载入动态链接库。此种方法可调用类方法。可执行程序静态加载动态链接库需要三个文件 x.dll, x.lib, x.h
可执行程序的头文件加入:
#include "x.h"

#pragma comment(lib,“x.lib”)

  编译时还要附加库目录,防止程序编译时无法找到x.dll。2.另一种是动态加载,只需要x.dll文件,在程序执行需要该动态链接库的地方加载x.dll。然后获取需要的x.dll库里面的函数或数据。该方法不能调用类方法。可执行程序调用了动态链接库,其运行不能缺少动态链接库。

  静态链接库:创建一个静态链接库,会生成x.lib文件。想要调用静态链接库里面的内容需要x.lib文件和x.h文件。库中内容会直接编译到x.exe文件中。可执行程序使用静态库编译成x.exe后,x.exe的运行就不在需要静态链接库了,可以独立运行了。

1.3 汇编

  汇编过程就是把汇编语言翻译成目标机器指令的过程,生成目标文件(.obj .o等)。目标文件中存放的也就是与源程序等效的目标的机器语言代码。

  目标文件由段组成,通常至少有两个段:

  • 代码段:包换主要程序的指令。该段是可读和可执行的,一般不可写
  • 数据段:存放程序用到的全局变量或静态数据。可读、可写、可执行。

  这个过程将.s文件转化成.o文件。(后者是二进制格式,且会为每一个源文件产生一个目标文件),命令:

as test.s -o test.o

   or

gcc -c test.s -o test.o
1.4 链接

  由汇编程序生成的目标文件并不能立即就执行(a.某个源文件调用了另一个源文件中的函数或常量;b.在程序中调用了某个库文件中的函数),还要通过链接过程。

  链接程序的主要工作就是将有关的目标文件连接起来。这个过程将.o文件转化成可执行的文件。

ld -o test.out test.o inc/mymath.o ...libraries...
2. 总结

在这里插入图片描述

在这里插入图片描述

  预编译处理(.c) -> 编译、优化程序(.s)->汇编程序(.obj、.o、.a、.ko) -> 链接程序(.exe、.elf、.axf等),通常:

$ gcc hello.c # 编译
$ ./a.out # 执行
3. 各种文件
  • .exe :可执行文件,点击即可运行
  • .ilk :当选定渐增型编译链接时,连接器自动生成ILK文件,记录链接信息
  • .obj :目标文件,obj文件与cpp文件名字一一对应
  • .pch :prcompiled-header,预编译头文件
  • .idb :文件保存的信息,使编译器在重新编译的时候只重新编译改动过的函数和最新类定义改动过的源文件,这样可提高编译速度
  • .pdb :Program Database,即程序数据库文件,用来记录调试信息
  • .dsp :Developer Studio Project,配置文件
  • .ncb :No Compile Browser,其中存放了供ClassView、WizardBar和Component Gallery使用的信息,由VC开发环境自动生成
  • .plg :超文本文件,可以用IE打开,记录build的过程
  • .cpp :C++源文件

欢迎扫描二维码关注微信公众号 深度学习与数学 [每天获取免费的大数据、AI等相关的学习资源、经典和最新的深度学习相关的论文研读,算法和其他互联网技能的学习,概率论、线性代数等高等数学知识的回顾]
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值