当前位置: 首页 > news >正文

C++基础:clang的分步编译-了解build细节

文章目录

  • Clang
  • 预处理器(Preprocessor)
  • 编译器(compiler)
  • 汇编器(assembler)
  • 链接器(Linker)

Clang

Clang(发音为/ˈklæŋ/类似英文单字clang) 是一个C、C++、Objective-C和Objective-C++编程语言的编译器前端。它采用了LLVM作为其后端,由LLVM2.6开始,一起发布新版本。它的目标是提供一个GNU编译器套装(GCC)的替代品,支持了GNU编译器大多数的编译设置以及非官方语言的扩展。作者是克里斯·拉特纳(Chris Lattner),在苹果公司的赞助支持下进行开发,而源代码许可是使用类BSD的伊利诺伊大学厄巴纳-香槟分校开源码许可。
Clang项目包括Clang前端和Clang静态分析器等。

之前我非常好奇,编程中从编写源代码到可执行文件的过程,很多集成开发环 (IDE)(比如Code::Blocks ,Microsoft Virtual Studio,Dev-Cpp等)都是先build一下,再run就完事。详细一点的涉及到编译器(Compiler) 和链接器(Linker),虽然又看了很多更为详细的文章,但始终还是缺乏实践操作,在捣鼓clang这玩意的时候,发现了竟然还有分步编译,这使得我能上手操作,真正直观感受一下这一过程:源代码(source code) 预处理器(preprocessor)编译器(compiler)→ 汇编程序(assembler)目标代码(object code)链接器(linker)可执行文件(executables),最后打包好的文件就可以给电脑去判读执行了。

下面看看Clang的分步编译是怎么搞得

# 预处理(Preprocessing)
clang++ -E hello.cpp -o hello.ii# 编译为汇编代码(Compilation)
clang++ -S hello.ii -o hello.s# 汇编为目标文件(Assembly)
clang++ -c hello.s -o hello.o# 链接为可执行文件(Linking)
clang++ hello.o -o hello

先温习一下几个术语:
源代码(英语:source code),是指一系列人类可读的计算机语言指令
目标代码(英语:Object code)指计算机科学中编译器或汇编器处理源代码后所生成的代码,它一般由机器代码或接近于机器语言的代码组成。[1]目标文件(英语:Object file)即存放目标代码的计算机文件,它常被称作二进制文件(Binaries)。
字节码(英语:Bytecode)通常指的是已经经过编译,但与特定机器代码无关,需要解释器转译后才能成为机器代码的中间代码。字节码通常不像源码一样可以让人阅读,而是编码后的数值常量、引用、指令等构成的序列。
机器语言(machine language)是一种指令集的体系。这种指令集称为机器代码(machine code),是计算机的CPU或GPU可直接解读的资料。

预处理器(Preprocessor)

在计算机科学中,预处理器是程序中处理输入数据,产生能用来输入到其他程序的数据的程序。继承于C语言的C++的C预处理器,是将采用以’#'为行首的指示,将相关的代码包含进来。
main.cpp

// 将 #include <iostream> 替换为iostream头文件的全部内容
// 可能从几行代码变成几千行代码
#include <iostream>
void a()
{std::cout << " a here " << '\n';
}void b()
{a();
}void c()
{a();b();
}void d()
{a();b();c();
}int main()
{d();return 0;
}

使用

clang++ -E main.cpp -o main.ii 

调用预处理器处理源文件main.cpp之后,我打开处理之后的main.ii看了看,加上原来的总共得有37040行代码,下面截取了开头和末尾一部分代码,应该是#include 告诉编译器把C++标准库iostream里面的内容包含进来了。
在这里插入图片描述

main.ii

# 1 "main.cpp"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 468 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "main.cpp" 2
# 1 "/usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/iostream" 1 3
# 37 "/usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/iostream" 3# 1 "/usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/requires_hosted.h" 1 3
# 31 "/usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/requires_hosted.h" 3
# 1 "/usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/x86_64-linux-gnu/c++/14/bits/c++config.h" 1 3
# 34 "/usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/x86_64-linux-gnu/c++/14/bits/c++config.h" 3
# 308 "/usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/x86_64-linux-gnu/c++/14/bits/c++config.h" 3
......
void a()
{std::cout << " a here " << '\n';
}void b()
{a();
}void c()
{a();b();
}void d()
{a();b();c();
}int main()
{d();return 0;
}

我注意到 iostream 头文件的实际路径是 “/usr/lib/gcc/x86_64-linux-gnu/14/…/…/…/…/include/c++/14/iostream”。然后我尝试切换工作目录(cd)到了 “/usr/lib/gcc/x86_64-linux-gnu/14/include/”,但我发现这个路径下并没有我要找的东西,或者这个路径本身不存在。我感到困惑。经过查询原来中间的…也代表当前目录的父目录,于是我算了一下,有4组…,从/14推到了/usr下,然后输入后面的信息,终于找到了iostream
在这里插入图片描述
iostream (only read) 共计88行

  1 // Standard iostream objects -*- C++ -*-23 // Copyright (C) 1997-2024 Free Software Foundation, Inc.4 //5 // This file is part of the GNU ISO C++ Library.  This library is free6 // software; you can redistribute it and/or modify it under the7 // terms of the GNU General Public License as published by the8 // Free Software Foundation; either version 3, or (at your option)......33 #ifndef _GLIBCXX_IOSTREAM34 #define _GLIBCXX_IOSTREAM 13536 #pragma GCC system_header3738 #include <bits/requires_hosted.h> // iostreams3940 #include <bits/c++config.h>41 #include <ostream>42 #include <istream>......67 #ifdef _GLIBCXX_USE_WCHAR_T68   extern wistream wcin;     ///< Linked to standard input69   extern wostream wcout;    ///< Linked to standard output70   extern wostream wcerr;    ///< Linked to standard error (unbuffered)71   extern wostream wclog;    ///< Linked to standard error (buffered)72 #endif73   ///@}7475   // For construction of filebuffers for cout, cin, cerr, clog et. al.76   // When the init_priority attribute is usable, we do this initialization77   // in the compiled library instead (src/c++98/globals_io.cc).78 #if !(_GLIBCXX_USE_INIT_PRIORITY_ATTRIBUTE \79       && __has_attribute(__init_priority__))80   static ios_base::Init __ioinit;81 #elif defined(_GLIBCXX_SYMVER_GNU) && defined(__ELF__)82   __extension__ __asm (".globl _ZSt21ios_base_library_initv");83 #endif8485 _GLIBCXX_END_NAMESPACE_VERSION86 } // namespace8788 #endif /* _GLIBCXX_IOSTREAM */....

从38-42,iostream文件还有其他的依赖库,或许闲了可以探索一下这些库之间的依赖关系。接下来看看编译器又将main.ii带向何方?

编译器(compiler)

编译器(compiler)是一种计算机程序,它会将某种编程语言写成的源代码(原始语言)转换成另一种编程语言(目标语言)。

它主要的目的是将便于人编写、阅读、维护的高级计算机语言所写作的源代码程序,翻译为计算机能解读、运行的低阶机器语言的程序,也就是可执行文件。编译器将原始程序(source program)作为输入,翻译产生使用目标语言(target language)的等价程序。源代码一般为高级语言(High-level language),如Pascal、C、C++、C# 、Java等,而目标语言则汇编语言目标机器的目标代码(Object code),有时也称作机器代码(Machine code)。

使用

clang++ -S main.ii -o main.s

将预处理的main.ii文件编译成汇编代码main.s,在shell上打开main.s看看汇编是怎么样的,它是一种符号语言。
main.s

  1 .text2     .file   "main.cpp"3                                         # Start of file scope inline assembly4     .globl  _ZSt21ios_base_library_initv56                                         # End of file scope inline assembly7     .globl  _Z1av                           # -- Begin function _Z1av8     .p2align    4, 0x909     .type   _Z1av,@function10 _Z1av:                                  # @_Z1av11     .cfi_startproc12 # %bb.0:13     pushq   %rbp14     .cfi_def_cfa_offset 1615     .cfi_offset %rbp, -1616     movq    %rsp, %rbp17     .cfi_def_cfa_register %rbp18     movq    _ZSt4cout@GOTPCREL(%rip), %rdi19     leaq    .L.str(%rip), %rsi20     callq   _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLT21     movq    %rax, %rdi22     movl    $10, %esi23     callq   _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_c@PLT24     popq    %rbp25     .cfi_def_cfa %rsp, 826     retq27 .Lfunc_end0:28     .size   _Z1av, .Lfunc_end0-_Z1av29     .cfi_endproc30                                         # -- End function31     .globl  _Z1bv                           # -- Begin function _Z1bv32     .p2align    4, 0x9033     .type   _Z1bv,@function34 _Z1bv:                                  # @_Z1bv35     .cfi_startproc36 # %bb.0:37     pushq   %rbp38     .cfi_def_cfa_offset 1639     .cfi_offset %rbp, -1640     movq    %rsp, %rbp41     .cfi_def_cfa_register %rbp42     callq   _Z1av43     popq    %rbp44     .cfi_def_cfa %rsp, 845     retq46 .Lfunc_end1:47     .size   _Z1bv, .Lfunc_end1-_Z1bv48     .cfi_endproc49                                         # -- End function50     .globl  _Z1cv                           # -- Begin function _Z1cv51     .p2align    4, 0x9052     .type   _Z1cv,@function53 _Z1cv:                                  # @_Z1cv54     .cfi_startproc55 # %bb.0:56     pushq   %rbp57     .cfi_def_cfa_offset 1658     .cfi_offset %rbp, -1659     movq    %rsp, %rbp60     .cfi_def_cfa_register %rbp61     callq   _Z1av62     callq   _Z1bv63     popq    %rbp64     .cfi_def_cfa %rsp, 865     retq66 .Lfunc_end2:67     .size   _Z1cv, .Lfunc_end2-_Z1cv68     .cfi_endproc69                                         # -- End function70     .globl  _Z1dv                           # -- Begin function _Z1dv71     .p2align    4, 0x9072     .type   _Z1dv,@function73 _Z1dv:                                  # @_Z1dv74     .cfi_startproc75 # %bb.0:76     pushq   %rbp77     .cfi_def_cfa_offset 1678     .cfi_offset %rbp, -1679     movq    %rsp, %rbp80     .cfi_def_cfa_register %rbp81     callq   _Z1av82     callq   _Z1bv83         callq   _Z1cv84         popq    %rbp85         .cfi_def_cfa %rsp, 886         retq87     .Lfunc_end3:88         .size   _Z1dv, .Lfunc_end3-_Z1dv89         .cfi_endproc90                                             # -- End function91         .globl  main                            # -- Begin function main92         .p2align    4, 0x9093         .type   main,@function94     main:                                   # @main95         .cfi_startproc96     # %bb.0:97         pushq   %rbp98         .cfi_def_cfa_offset 1699         .cfi_offset %rbp, -16
100         movq    %rsp, %rbp
101         .cfi_def_cfa_register %rbp
102         subq    $16, %rsp
103         movl    $0, -4(%rbp)
104         callq   _Z1dv
105         xorl    %eax, %eax
106         addq    $16, %rsp
107         popq    %rbp
108         .cfi_def_cfa %rsp, 8
109         retq
110     .Lfunc_end4:
111         .size   main, .Lfunc_end4-main
112         .cfi_endproc
113                                         # -- End function
114     .type   .L.str,@object                  # @.str
115     .section    .rodata.str1.1,"aMS",@progbits,1
116 .L.str:
117     .asciz  " a here "
118     .size   .L.str, 9
119
120     .ident  "Debian clang version 19.1.7 (3+b1)"
121     .section    ".note.GNU-stack","",@progbits
122     .addrsig
123     .addrsig_sym _Z1av
124     .addrsig_sym _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_c
125     .addrsig_sym _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
126     .addrsig_sym _Z1bv
127     .addrsig_sym _Z1cv
128     .addrsig_sym _Z1dv
129     .addrsig_sym _ZSt4cout

上面有很多代码,但是基本都是重复的,提取出来就是下面的代码,根据ai和自己的理解查了查了意思。

1     .text                           # 将后续代码放入可执行代码段(text section)
2     .file   "main.cpp"              # 指明源文件名,用于调试信息
3                                         # Start of file scope inline assembly
4     .globl  _ZSt21ios_base_library_initv # 声明全局符号(通常是C++运行时库初始化例程)
5
6                                         # End of file scope inline assembly
7     .globl  _Z1av                   # 声明函数a()为全局符号,允许其他文件调用
8     .p2align    4, 0x90             # 16字节对齐(2^4=16),用0x90(NOP指令)填充空隙
9     .type   _Z1av,@function         # 声明符号_Z1av的类型是函数
10 _Z1av:                              # 函数a()的入口点(标签)
11     .cfi_startproc                 # 开始生成调用帧信息(CFI),用于调试和异常处理
12 # %bb.0:                            # 基本块0的标签(由编译器生成的控制流标记)
13     pushq   %rbp                   # 保存调用者的栈帧指针到栈上
14     .cfi_def_cfa_offset 16         # CFI: 规范帧地址(CFA)现在在原始SP+16的位置
15     .cfi_offset %rbp, -16          # CFI: 旧的RBP值保存在CFA-16的位置
16     movq    %rsp, %rbp             # 建立新的栈帧:将当前栈指针复制到RBP
17     .cfi_def_cfa_register %rbp     # CFI: 现在使用RBP作为计算CFA的基准寄存器
18     movq    _ZSt4cout@GOTPCREL(%rip), %rdi # 获取std::cout地址放入RDI(第一个参数)
19     leaq    .L.str(%rip), %rsi     # 计算字符串" a here "的地址放入RSI(第二个参数)
20     callq   _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLT # 调用operator<<输出字符串
21     movq    %rax, %rdi             # 将返回值(cout引用)作为下一次调用的第一个参数
22     movl    $10, %esi              # 将数字10(换行符'\n'的ASCII码)放入ESI(第二个参数)
23     callq   _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_c@PLT # 调用operator<<输出字符
24     popq    %rbp                   # 从栈中恢复调用者的栈帧指针
25     .cfi_def_cfa %rsp, 8           # CFI: 现在CFA在RSP+8的位置(指向返回地址之前)
26     retq                           # 从函数返回
27 .Lfunc_end0:                        # 函数结束的标签
28     .size   _Z1av, .Lfunc_end0_Z1av # 定义函数大小(结束地址-开始地址)
29     .cfi_endproc                   # 结束调用帧信息(CFI)记录

7行.globl是全局可见的意思,作用类似于c++中的全局变量,就是什么地方都可以访问它,_Z1av 就是指代标识符a,这就是为什么同一个作用域中函数名,变量名要唯一,目的是方便编译器识别。
9行 .type _Z1av,@function是关键字.type进一步告诉编译器_Z1av(a)是一个函数。
10行和27行是a函数开始和结束的地方,对应着a函数的左边花括号‘{’ 和右花括号‘}’的位置。
11行和27行是调用栈开始和结束的地方。我们听说一个函数就是一个栈,能够自动创建和销毁。而new出来的就是堆上面,需要手动创建和销毁,因此常常有跟指针相关的问题。
//todo 这里对汇编代码有点问题,稍后解决。

汇编器(assembler)

汇编语言(英语:assembly language或assembler language)是任何一种用于电子计算机、微处理器、微控制器,或其他可编程器件的低级语言。在不同的设备中,汇编语言对应着不同的机器语言指令集。
使用汇编语言编写的源代码,然后通过相应的汇编程序将它们转换成可执行的机器代码(或者称为目标代码)。这一过程被称为汇编过程
典型的现代汇编器(assembler)建造目标代码,由解译组语指令集的助记符(Mnemonics)到操作码,并解析符号名称(Symbolic names)成为存储器地址以及其它的实体。

目标代码(英语:Object code)指计算机科学中编译器或汇编器处理源代码后所生成的代码,它一般由机器代码或接近于机器语言的代码组成。[1]目标文件(英语:Object file)即存放目标代码的计算机文件,它常被称作二进制文件(Binaries)。

在shell上使用命令行

clang++ -c main.s -o main.o

汇编语言main.s 就会编译成目标代码main.o,下面打开main.o,我也懵逼了,这是啥东西,我用vim显示行数为7行,但是我连它怎么分行的都不清楚。
main.o

  1 ^?ELF^B^A^A^@^@^@^@^@^@^@^@^@^A^@>^@^A^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@ ^E^@^@^@^@^@^@^@^@^@^@@^@^@^@^@^@@^@^K^    @^A^@UH<89>åH<8b>=^@^@^@^@H<8d>5^@^@^@^^@^@^@^@H<89>Ǿ2 ^@^@^^@^@^@^@]Ãf.^O^_<84>^@^@^@^@^@UH<89>åè^@^@^@^@]Ã^O^_D^@^@UH<89>åè^@^@^@^^@^@^@^@]ÃUH<89>åè^@^@^@^^@^@^@^@    è^@^@^@^@]Ãf.^O^_<84>^@^@^@^@^@<90>UH<89>åH<83>ì^PÇEü^@^@^@^^@^@^@^@1ÀH<83>Ä^P]à a here ^@^@Debian clang version 1    9.1.7 (3+b1)^@^@^@^@^@^@^@^@^T^@^@^@^@^@^@^@^AzR^@^Ax^P^A^[^L^G^H<90>^A^@^@^\^@^@^@^\^@^@^@^@^@^@^@&^@^@^@^@A^N^P<86    >^BC^M^Fa^L^G^H^@^@^@^\^@^@^@<^@^@^@^@^@^@^@^K^@^@^@^@A^N^P<86>^BC^M^FF^L^G^H^@^@^@^\^@^@^@\^@^@^@^@^@^@^@^P^@^@^@^@    A^N^P<86>^BC^M^FK^L^G^H^@^@^@^\^@^@^@|^@^@^@^@^@^@^@^U^@^@^@^@A^N^P<86>^BC^M^FP^L^G^H^@^@^@^\^@^@^@<9c>^@^@^@^@^@^@^    @^\^@^@^@^@A^N^P<86>^BC^M^FW^L^G^H^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@[^@^@^@^D^@ñÿ^@^@^@^@^@^@^@^    @^@^@^@^@^@^@^@^@^@^@^@^@^C^@^B^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@T^@^@^@^A^@^D^@^@^@^@^@^@^@^@^@    ^@^@^@^@^@^@^@^A    ^@^@^@^P^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@0^@^@^@^R^@^B^@^@^@^@^@^@^@^@^@&^@^@^@^@^@^@^@A^@^@^@^P^@^@^@^@^@^@^@^    @^@^@^@^@^@^@^@^@^@^@^^@^@^@^P^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@<96>^@^@^@^P^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@    ^@^@*^@^@^@^R^@^B^@0^@^@^@^@^@^@^@^K^@^@^@^@^@^@^@$^@^@^@^R^@^B^@@^@^@^@^@^@^@^@^P^@^@^@^@^@^@^@^^^@^@^@^R^@^B^@P^@^    @^@^@^@^@^@^U^@^@^@^@^@^@^@d^@^@^@^R^@^B^@p^@^@^@^@^@^@^@^\^@^@^@^@^@^@^@^G^@^@^@^@^@^@^@*^@^@^@^F^@^@^@üÿÿÿÿÿÿÿ^N^@    ^@^@^@^@^@^@^B^@^@^@^C^@^@^@üÿÿÿÿÿÿÿ^S^@^@^@^@^@^@^@^D^@^@^@^G^@^@^@üÿÿÿÿÿÿÿ ^@^@^@^@^@^@^@^D^@^@^@^H^@^@^@üÿÿÿÿÿÿÿ5    ^@^@^@^@^@^@^@^D^@^@^@^E^@^@^@üÿÿÿÿÿÿÿE^@^@^@^@^@^@^@^D^@^@^@^E^@^@^@üÿÿÿÿÿÿÿJ^@^@^@^@^@^@^@^D^@^@^@    ^@^@^@üÿÿÿÿÿ    ÿÿU^@^@^@^@^@^@^@^D^@^@^@^E^@^@^@üÿÿÿÿÿÿÿZ^@^@^@^@^@^@^@^D^@^@^@    ^@^@^@üÿÿÿÿÿÿÿ_^@^@^@^@^@^@^@^D^@^@^@3 ^@^@^@üÿÿÿÿÿÿÿ<80>^@^@^@^@^@^@^@^D^@^@^@^K^@^@^@üÿÿÿÿÿÿÿ ^@^@^@^@^@^@^@^B^@^@^@^B^@^@^@^@^@^@^@^@^@^@^@@^@^@^@^@^@^@    ^@^B^@^@^@^B^@^@^@0^@^@^@^@^@^@^@`^@^@^@^@^@^@^@^B^@^@^@^B^@^@^@@^@^@^@^@^@^@^@<80>^@^@^@^@^@^@^@^B^@^@^@^B^@^@^@P^@    ^@^@^@^@^@^@ ^@^@^@^@^@^@^@^B^@^@^@^B^@^@^@p^@^@^@^@^@^@^@^E^H^G4 ^K^F^@_ZSt21ios_base_library_initv^@_Z1dv^@_Z1cv^@_Z1bv^@_Z1av^@.rela.text^@_ZSt4cout^@.comment^@.L.str^@main.cpp^@m    ain^@.note.GNU-stack^@.llvm_addrsig^@.rela.eh_frame^@_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_c^@_ZStlsIS    t11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc^@.strtab^@.symtab^@.rodata.str1.1^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^    @^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^D^A^@^@^    C^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@w^D^@^@^@^@^@^@#^A^@^@^@^@^@^@^@^@^@^@^@^@^@^@^A^@^@^@^@^@^@^@^@^@^@^@^@^@^@^    @;^@^@^@^A^@^@^@^F^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@@^@^@^@^@^@^@^@<8c>^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^P^@^@^@^@^@^@^@^@^    @^@^@^@^@^@^@6^@^@^@^D^@^@^@@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^^B^@^@^@^@^@^@^H^A^@^@^@^@^@^@@                                                                                                                       @                                                                                                                       @  5 ^@^@^@^B^@^@^@^H^@^@^@^@^@^@^@^X^@^@^@^@^@^@^@^T^A^@^@^A^@^@^@2^@^@^@^@^@^@^@^@^@^@^@^@^@^@^^@^@^@^@^@^@^@    ^@^@    ^@^@^@^@^@^@^@^@^@^@^@^@^@^A^@^@^@^@^@^@^@^A^@^@^@^@^@^@^@K^@^@^@^A^@^@^@0^@^@^@^@^@^@^@^@^@^@^@^@^@^@^^@^@^@^@^@^    @^@$^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^A^@^@^@^@^@^@^@^A^@^@^@^@^@^@^@i^@^@^@^A^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^^@    ^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^A^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@<8c>^@^@^@^A^@^@p^B^@^@^@^@^@^@^@^@^@^@^    @^@^@^@^@^@^A^@^@^@^@^@^^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^H^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@<87>^@^@^@^D^@^@^@@^@^@^@^@^    @^@^@^@^@^@^@^@^@^@^^C^@^@^@^@^@^@x^@^@^@^@^@^@^@6 ^@^@^@^G^@^@^@^H^@^@^@^@^@^@^@^X^@^@^@^@^@^@^@y^@^@^@^CLÿo^@^@^@<80>^@^@^@^@^@^@^@^@^@^@^@^@p^D^@^@^@^@^@^@^G^@^@^@^    @^@^@^@7 ^@^@^@^@^@^@^@^A^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^L^A^@^@^B^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^^A^@^@^@^@^@^@8^A^@^@    ^@^@^@^@^A^@^@^@^D^@^@^@^H^@^@^@^@^@^@^@^X^@^@^@^@^@^@^@

最后一步就是链接器了,它做完最后的处理就会生成可执行文件,放到windows上就是exe文件,当点击exe文件运行时候出现的错误,我们叫做运行错误。

链接器(Linker)

链接器的作用是合并所有目标文件,并生成所需的输出文件(例如,可以运行的可执行文件)。这个过程称为链接。如果链接过程中的任何步骤失败,链接器都会生成一条描述该问题的错误消息,然后中止运行。

首先,链接器读取编译器生成的(经过前面三个步骤)每个目标文件并确保它们有效。

其次,链接器确保所有跨文件依赖关系都得到正确解析。例如,如果您在一个 .cpp 文件中定义了某个内容,然后在另一个 .cpp 文件中使用它,链接器会将这两个内容连接起来。如果链接器无法将对某个内容的引用与其定义连接起来,则会收到链接器错误,并且链接过程将中止。

第三,链接器通常链接一个或多个库文件,这些库文件是已“打包”以供其他程序重用的预编译代码的集合。

最后,链接器输出所需的输出文件。通常,这是一个可以启动的可执行文件(但如果您的项目设置了库文件,也可以是一个库文件)。

在shell上使用命令行

clang++ main.o -o main

链接器就会将目标代码main.o处理成可执行文件main,同样也晦涩难懂。
main

^?ELF^B^A^A^@^@^@^@^@^@^@^@^@^C^@>^@^A^@^@^@`^P^@^@^@^@^@^@@^@^@^@^@^@^@^@89^@^@^@^@^@^@^@^@^@^@@^@8^@^N^@@^@^_^@^^^    @^F^@^@^@^D^@^@^@@^@^@^@^@^@^@^@@^@^@^@^@^@^@^@@^@^@^@^@^@^@^@^P^C^@^@^@^@^@^@^P^C^@^@^@^@^@^@^H^@^@^@^@^@^@^@^C^@^@    ^@^D^@^@^@<94>^C^@^@^@^@^@^@<94>^C^@^@^@^@^@^@<94>^C^@^@^@^@^@^@^\^@^@^@^@^@^@^@^\^@^@^@^@^@^@^@^A^@^@^@^@^@^@^@^A^@    ^@^@^D^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@¨^G^@^@^@^@^@^@¨^G^@^@^@^@^@^@^@^P^@^@^@^@^@^@^A^@^@^@^E    ^@^@^@^@^P^@^@^@^@^@^@^@^P^@^@^@^@^@^@^@^P^@^@^@^@^@^@å^A^@^@^@^@^@^@å^A^@^@^@^@^@^@^@^P^@^@^@^@^@^@^A^@^@^@^D^@^@^@    ^@ ^@^@^@^@^@^@^@ ^@^@^@^@^@^@^@ ^@^@^@^@^@^@¬^A^@^@^@^@^@^@¬^A^@^@^@^@^@^@^@^P^@^@^@^@^@^@^A^@^@^@^F^@^@^@<98>-^@^@    ^@^@^@^@<98>=^@^@^@^@^@^@<98>=^@^@^@^@^@^@<88>^B^@^@^@^@^@^@<90>^B^@^@^@^@^@^@^@^P^@^@^@^@^@^@^B^@^@^@^F^@^@^@¨-^@^@    ^@^@^@^@¨=^@^@^@^@^@^@¨=^@^@^@^@^@^@^P^B^@^@^@^@^@^@^P^B^@^@^@^@^@^@^H^@^@^@^@^@^@^@^D^@^@^@^D^@^@^@P^C^@^@^@^@^@^@P    ^C^@^@^@^@^@^@P^C^@^@^@^@^@^@ ^@^@^@^@^@^@^@ ^@^@^@^@^@^@^@^H^@^@^@^@^@^@^@^D^@^@^@^D^@^@^@p^C^@^@^@^@^@^@p^C^@^@^@^    @^@^@p^C^@^@^@^@^@^@$^@^@^@^@^@^@^@$^@^@^@^@^@^@^@^D^@^@^@^@^@^@^@^D^@^@^@^D^@^@^@<8c>!^@^@^@^@^@^@<8c>!^@^@^@^@^@^@    <8c>!^@^@^@^@^@^@ ^@^@^@^@^@^@^@ ^@^@^@^@^@^@^@^D^@^@^@^@^@^@^@Såtd^D^@^@^@P^C^@^@^@^@^@^@P^C^@^@^@^@^@^@P^C^@^@^@^@    ^@^@ ^@^@^@^@^@^@^@ ^@^@^@^@^@^@^@^H^@^@^@^@^@^@^@Påtd^D^@^@^@^P ^@^@^@^@^@^@^P ^@^@^@^@^@^@^P ^@^@^@^@^@^@L^@^@^@^@    ^@^@^@L^@^@^@^@^@^@^@^D^@^@^@^@^@^@^@Qåtd^F^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^    @^@^@^@^@^@^@^P^@^@^@^@^@^@^@Råtd^D^@^@^@<98>-^@^@^@^@^@^@<98>=^@^@^@^@^@^@<98>=^@^@^@^@^@^@h^B^@^@^@^@^@^@h^B^@^@^@    ^@^@^@^A^@^@^@^@^@^@^@^D^@^@^@^P^@^@^@^E^@^@^@GNU^@^B<80>^@À^D^@^@^@^A^@^@^@^@^@^@^@^D^@^@^@^T^@^@^@^C^@^@^@GNU^@^X^    h´_qõ×u°^O1Û6PNÓl[G/lib64/ld-linux-x86-64.so.2^@^B^@^@^@    ^@^@^@^A^@^@^@^F^@^@^@^@^@<81>^@^@^@^@^@    ^@^@^@^@^@^@    ^@ÑeÎm^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@Û^@^@^@^R^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@<99>^@^    @^@^R^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@F^@^@^@^R^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@Ñ^@^@^@^Q^@^@^@^@^@^@^@^@^    @^@^@^@^@^@^@^@^@^@^@|^@^@^@^R^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^P^@^@^@ ^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^    A^@^@^@ ^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@,^@^@^@ ^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@í^@^@^@"^@^@^@^@^@^@^@^@    ^@^@^@^@^@^@^@^@^@^@^@^@__gmon_start__^@_ITM_deregisterTMCloneTable^@_ITM_registerTMCloneTable^@_ZStlsISt11char_trai    tsIcEERSt13basic_ostreamIcT_ES5_c^@_ZSt21ios_base_library_initv^@_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5    _PKc^@_ZSt4cout^@__libc_start_main^@__cxa_finalize^@libstdc++.so.6^@libm.so.6^@libgcc_s.so.1^@libc.so.6^@GLIBCXX_3.4    .32^@GLIBCXX_3.4^@GLIBC_2.34^@GLIBC_2.2.5^@^@^@^@^C^@^D^@^D^@^D^@^E^@^A^@^A^@^A^@^B^@^@^@^@^@^A^@^B^@ü^@^@^@^P^@^@^@    0^@^@^@Bø<97>^B^@^@^E^@-^A^@^@^P^@^@^@t)<92>^H^@^@^D^@<^A^@^@^@^@^@^@^A^@^B^@#^A^@^@^P^@^@^@^@^@^@^@´<91><96>^F^@^@^    C^@H^A^@^@^P^@^@^@u^Zi  ^@^@^B^@S^A^@^@^@^@^@^@<98>=^@^@^@^@^@^@^H^@^@^@^@^@^@^@@^Q^@^@^@^@^@^@ =^@^@^@^@^@^@^H^@^@^    @^@^@^@^@^@^Q^@^@^@^@^@^@^X@^@^@^@^@^@^@^H^@^@^@^@^@^@^@^X@^@^@^@^@^@^@¸?^@^@^@^@^@^@^F^@^@^@   ^@^@^@^@^@^@^@^@^@^@    ^@À?^@^@^@^@^@^@^F^@^@^@^A^@^@^@^@^@^@^@^@^@^@^@È?^@^@^@^@^@^@^F^@^@^@^D^@^@^@^@^@^@^@^@^@^@^@Ð?^@^@^@^@^@^@^F^@^@^@    ^F^@^@^@^@^@^@^@^@^@^@^@Ø?^@^@^@^@^@^@^F^@^@^@^G^@^@^@^@^@^@^@^@^@^@^@à?^@^@^@^@^@^@^F^@^@^@^H^@^@^@^@^@^@^@^@^@^@^@

在shell上我们来执行一下 main

./main

会输出

 a herea herea herea here

文章转载自:

http://ciZetGPm.kfyjh.cn
http://hhsrSviO.kfyjh.cn
http://EjaBPqrt.kfyjh.cn
http://4MjdZgRr.kfyjh.cn
http://dEMDAim1.kfyjh.cn
http://FsHYt5Ah.kfyjh.cn
http://5eu79Z3W.kfyjh.cn
http://9QFvGomh.kfyjh.cn
http://uWYZkPBq.kfyjh.cn
http://7UFIad6R.kfyjh.cn
http://PTptiWGt.kfyjh.cn
http://MaTMdT4Q.kfyjh.cn
http://5xv74swk.kfyjh.cn
http://7aINSmi6.kfyjh.cn
http://XvbD0Rid.kfyjh.cn
http://qPZ5gy2T.kfyjh.cn
http://6UnuJXbp.kfyjh.cn
http://ocuiTdCi.kfyjh.cn
http://6dLGQ9rS.kfyjh.cn
http://SC07kNLd.kfyjh.cn
http://kPnKOaPo.kfyjh.cn
http://c8srMGst.kfyjh.cn
http://xQRApek2.kfyjh.cn
http://vSilrOle.kfyjh.cn
http://sVxJ2S37.kfyjh.cn
http://nqazHHri.kfyjh.cn
http://pHaHx4Od.kfyjh.cn
http://mK24mr6l.kfyjh.cn
http://iLUvJJ3t.kfyjh.cn
http://LhKcKWG7.kfyjh.cn
http://www.dtcms.com/a/385712.html

相关文章:

  • OS-内存管理 真题复盘总结
  • 一阶低通滤波在运动控制中的应用
  • Redis核心数据类型解析
  • 在 Ubuntu 22.04 系统中,如何安装最新版 MySQL和Navicat Pro 17
  • 立创·庐山派K230CanMV开发板的进阶学习——颜色识别
  • 大模型学习:Transformer模型构建
  • C++ STL之deque的使用和模拟实现
  • 【网络安全就业】信息安全专业的就业前景(非常详细)零基础入门到精通,收藏这篇就够了
  • SpringBoot中的循环依赖以及解决办法
  • np.linalg 函数一览
  • 网络:UDP协议
  • 【开题答辩全过程】以 JAVA汽车年审管理系统为例,包含答辩的问题和答案
  • 【Linux网络编程】Socket-UDP
  • OpenCV物体跟踪:从理论到实战的全面解析
  • Linux:线程同步
  • Day24_【深度学习(3)—PyTorch使用(2)—张量的数值计算】
  • 9月15日
  • 【langchain】构建简单检索问答链
  • 简单的数组
  • ENVI系列教程(四)——图像几何校正
  • 数据结构基础--散列表
  • 【Redis】-- 主从复制
  • 输入1.8V~5.5V 输出28V DCDC升压芯片TLV61046A
  • Windows 上安装 FFmpeg 8.0(2025 版)——从“手动解压”到“一条命令”的进化之路
  • 红黑树(RBTree)知识总结
  • 若依框架前端通过 nginx docker 镜像本地运行
  • 二十、瑞萨RZT2N2 PROFINET SDK正式发布
  • SpringAI框架接入Deepseek和豆包实现智能聊天
  • 江协科技STM32课程笔记(一) —GPIO
  • 江协科技STM32课程笔记(二)—外部中断EXTI