makefile 学习(1):C/C++ 编译过程

1. GCC 介绍

1.1 介绍

GCC 官方文档 https://gcc.gnu.org/onlinedocs/

  • 官方文档是最权威的,网上所有的答案都来自官方文档
  • 国内论坛参差不齐,找到好的答案比较花时间,并且很容易被错误的文档误导。所以推荐看官方文档靠谱点,并且可以提升英语阅读能力

GCC(GNU Compiler Collection) GNU编译程序集合, 其他所有开放源码软件都在某种程度上依赖于它,甚至其他语言,例如Python都是由C语言开发,由GNU编译程序编译的。

1.2 GCC常见的组成部分

GCC是由很多组件组成的,常见的组成部分如下:

  • c++: 它是gcc的一个版本,默认语言设置为C++,而且在连接的时候自动包含标注C++库,这和g++一样。(c++与g++组件功能基本上一样的,我们更常用g++
  • configure: GCC源代码根目录中的一个脚本,用于设置配置值和创建GCC编译程序必须的make程序文件
  • gcc: 主要用来编译C语言程序,该驱动程序等同于执行编译程序和连接程序以产生需要的输出
  • g++: gcc的一个版本,默认语言设置为C++,而且在链接的时候自动包含标准的C++库,这和c++一样
  • libgcc: 该库包含的例程作为编译程序的一部分,是因为他们可被链接到实际的可执行程序中,它们是特殊的例程,链接到可执行程序,来执行基本的任务,例如浮点运算,这些库中的例程通常都是平台相关的
  • libstdc++:运行时库(动态库),包含定义为标准语言一部分的所有C++类和函数

1.3 GCC包含的常见软件

下面介绍的GCC常见软件,每一个软件和程序的编译和运行是密切相关的

  • ar: 这是一个程序,可通过从文档中增加、删除和析取文件来维护库文件。通过使用该工具是为了创建和管理连接程序使用的目标库文档。该程序是binutils包的一部分。
  • as: GNU的汇编器,它可以被编译或能够在各种不同平台上工作。
  • gdb: GNU的调试器 ,不管是用vscode 还是vs2015,在debug c++程序时,底层都是调用GNU调试器来执行调试任务。
  • gprof: 该程序会监督编译程序的执行过程,并报告程序中各个函数的运行时间,可以根据所提供的配置文件来优化程序。(在实际工作中,接触会比较少)
  • ld: GNU的连接程序,利用该程序将目标文件链接为可执行程序或者库文件(实际工作中用的非常多)
  • libtool: 一个基本库,支持make程序的描述文件使用的简化共享库用法的脚本。
  • make: 一个工具程序,它会读makefile脚本来确定程序的哪个部分需要编译和链接,然后发布必要的命令。它读的脚本(叫做makefile或者Makefile)定义了文件关系和依赖关系, 执行make进行自动批量编译程序,解放我们的双手。

1.4 GCC默认头文件搜索路径

  • 头文件对于编译来说是非常重要的,在编译程序时候,一定要把需要依赖的头文件的搜索路径都要Include进来。
  • 在github中下载一些项目上,经常会遇到一些非常头疼的问题。会经常报错显示找不到xx头文件, 在学会makefile编写后,相信遇到这样的问题,可以轻松的去解决。
  • 可以通过下面的命令来查看GCC默认的头文件搜索路径
echo | gcc -v -x c -E -
 /usr/lib/gcc/x86_64-linux-gnu/7/include
 /usr/local/include
 /usr/lib/gcc/x86_64-linux-gnu/7/include-fixed
 /usr/include/x86_64-linux-gnu
 /usr/include

在这里插入图片描述

2. 编译过程

2.1 hello world 在计算机中的表示

hello程序的生命周期是从一个源程序(或者说源文件)开始的,即程序员通过编译器创建并保存的文本文件,文件名为hello.c

源程序其实是由值0和1组成的位(又称为比特)序列。8个位被组织成一组,称为字节,每个字节表示程序中的某些文件字符。

一般.c后缀的是c语言脚本文件,.cpp后缀的是c++语言的脚本文件。但是在linux系统中,其实是不分文件后缀的,对它来说一个文件就是一串比特,没有任何别的意义,加后缀主要是使用者的习惯,为了更加方便的辨别是哪种语言编写的文件,对计算机来说是没有区别的。

大部分计算机使用ASCII标准来表示文本字符

  • 用一个唯一的单字节大小的整数值信息来表示每个字符
  • hello.c程序是以字节序列的方式存储在文件中的

hello.c 的表示方法说明了一个基本思想:系统中所有的信息——包括磁盘文件、内存中的程序、内存中存放的用户数据以及网络上传送的数据,都是由一串比特表示的

在这里插入图片描述

2.2 编译过程 程

  • 程序的生命周期是从一个高级的C/C++语言程序开始的。

  • 为了在系统中运行程序,以c语言的hello.c来说,每条C语句必须被底层程序转化为一系列低级的机器语言指令(010101的格式)。

  • 然后这些指令按照一种称为可执行目标程序的格式打包好,并以二进制磁盘文件的形式存放起来,目标程序也称为可执行目标文件。

  • GCC编译器读取程序文件hello.c, 并把它翻译成一个可执行目标文件hello, 这个编译过程可分为四个阶段完成,如下图所示,执行这四个阶段的程序(预处理器、编译器、汇编器和链接器)一起构成了编译系统(compilation system)。

【图】

2.2.1 预处理阶段

预处理器(cpp) 根据以字符#开头的命令(头文件), 修改原始的C程序。比如hello.c中第一行的#include <stdio.h>命令告诉预处理器读取系统头文件stdio.h中的内容,并把它直接插入到程序文本中,结果就得到了另一个C程序,通常是以.i作为文件扩展名。

2.2.2 编译阶段

编译器(ccl) 将文本文件hello.i翻译成文本文件hello.s, 它是一个汇编语言程序,该汇编程序对函数main的定义,如下所示:

						main:
						subq   $8, %rsp
						mov1   $.LCO,%edi
						call   puts
						mov1   $0,%eax
						addq   $8,%rsp
						ret

每条语句都以一种文本格式描述了一条低级机器语言指令。汇编语言非常有用,它为不同高级语言的不同编译器提供了通用的输出语言

2.2.3 汇编阶段

汇编器(as): 将hello.s编译成机器语言指令,把这些指令打包成一种叫做可重定位目标程序(relocation object program)的格式,并将结果保存在目标文件的hello.o中,

hello.o文件是一个二进制文件,它包含17个字节是函数main的指令编码。如果我们在文本编辑器中打开hello.o,将看到一堆乱码

2.2.4 链接阶段

注意,hello程序调用了printf函数,它是每个C编译器都提供的标准C库中的一个函数。printf函数存在于一个名为printf.o的单独的预编译好的目标文件中,而这个文件必须以某种方式合并到我们的hello.o程序中

连接器(ld) 就负责处理这种合并,并输出可执行文件hello文件,可以被加载到内存中,有系统执行。

3. 程序在计算机内的存储

系统会花费大量的时间和步骤把信息从一个地方挪到另一个地方:

  • hello 程序的机器指令最初是放在磁盘上的
  • 当程序加载时,他们被复制到主存
  • 当处理器运行程序时,指令又从主存中复制到处理器中

相似地,数据串hello world\n开始在磁盘上,然后被复制到主存,最后从主存上复制到显示设备。

猜你喜欢

转载自blog.csdn.net/weixin_38346042/article/details/130725717
今日推荐