乐趣区

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…

退出移动版