C1-编译和链接

单文件例子

先以单文件编译为例子 hello.cc:

#include<iostream>
using namespace std;

int main() {
    cout << "Hello World" << endl;
    return 0;
}

编译加运行的命令

g++ 'hello.cc' -o 'hello' -Wall -g -O2  -std=c++17 && hello

其含义就是将hello.cc源文件编译输出为hello这个可执行文件并运行, 得到一个”Hello World”的输出, 那么这个简单的 g++ 命令背后发生了什么呢?

编译过程

上述gcc命令其实依次执行了四步操作:

1.预处理(Preprocessing)

2.编译(Compilation)

3.汇编(Assemble)

4.链接(Linking)

常见后缀含义

.c为后缀的文件:c语言源代码文件

.a为后缀的文件:是由目标文件构成的库文件

.cpp为后缀的文件:是c++源代码文件

.h为后缀的文件:头文件

.o为后缀的文件:是编译后的目标文件

.s为后缀的文件:是汇编语言源代码文件

.m为后缀的文件:Objective-C原始程序

.so为后缀的文件:编译后的动态库文件

1.预处理(Preprocessing)

预处理阶段做的事情: 宏的替换,还有注释的消除,还有找到相关的库文件,将#include文件的全部内容插入。若用<>括起文件则在系统的INCLUDE目录中寻找文件,若用””括起文件则在当前目录中寻找文件。预处理之后得到的仍然是文本文件,但文件体积会大很多, 生成的文件是 -i 文件, 对hello.cc文件进行预处理的命令

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

或者直接调用cpp命令

cpp hello.cc -o hello.i

-E是指让编译器在预处理之后就退出,不进行后续编译过程, -o是指指定输出文件名, 在处理成.i文件以后, 文件的体积会大的多, 变成了4w多行的文件

2.编译(Compilation)

这里的编译不是指程序从源文件到二进制程序的全部过程,而是指将经过预处理之后的程序转换成特定汇编代码(assembly code)的过程。将.i预处理文件编译为汇编代码的命令是cc1, 使用-S可以直接从.cc文件到汇编代码

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

hello.s文件的部分内容

    .section    __TEXT,__text,regular,pure_instructions
    .build_version macos, 10, 14    sdk_version 10, 14
    .globl    _main                   ## -- Begin function main
    .p2align    4, 0x90
_main:                                  ## @main
    .cfi_startproc
## %bb.0:
    pushq    %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    subq    $16, %rsp

3.汇编(Assemble)

汇编过程将上一步的汇编代码转换成机器码(machine code),这一步产生的文件叫做目标文件,是二进制格式。gcc汇编过程通过as命令完成:

as hello.s -o hello.o

等价于

g++ -c hello.s -o hello.o

输出的hello.o为二进制文件
这一步会为每一个源文件产生一个目标文件

4.链接(Linking)

链接过程将多个目标文件以及所需的库文件(.so等)链接成最终的可执行文件(executable file)
命令大致如下

 ld -o test.out test.o inc/mymath.o ...libraries...

所以其实简单的一行命令

g++ hello.cc

其中其实包含了好几层流程

多文件例子

添加两个文件 math.h

`int add(int a, int b);`

math.cc, 定义一下这个add方法

int add(int a, int b) {
    return a + b;
};

然后在hello.cc中调用一下这个add方法

#include<iostream>
#include "math.h"
using namespace std;

int main() {
    cout << "Hello World" << endl;
    cout << add(1, 2) << endl;
    return 0;
}

再直接运行

g++ hello.cc

试试, 发现收到如下报错

Undefined symbols for architecture x86_64:
  "add(int, int)", referenced from:
      _main in hello-9842e2.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

看到说add(int, int)这个方法是未定义符号, 可是明明将math.h引入进来了啊, 其实是因为没有链接到math.cc生成的.o文件, 链接math.o再生成可执行文件试试

g++ -c hello.cc math.cc

这样生成了hello.o math.o两个二进制文件, 然后链接math.o和hello.o生成可执行文件

g++ hello.o math.o -o hello

再运行hello试试, 发现得到了想要的输出。或者还有一个更简单的办法

g++ hello.cc math.cc -o hello

经过我测试, 可以直接得到上面两个命令合并的效果。
上面的命令, 在中大型项目中, 一般使用makefile文件进行整合, makefile文件的作用和写法单独写一个文章。

参考链接

https://www.cnblogs.com/ericl…
https://blog.csdn.net/csdn912…

【腾讯云】轻量 2核2G4M,首年65元

阿里云限时活动-云数据库 RDS MySQL  1核2G配置 1.88/月 速抢

本文由乐趣区整理发布,转载请注明出处,谢谢。

您可能还喜欢...

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据