Linux编译器——gcc/g++的使用

目录

一、 背景知识:

1 程序的翻译环境与运行环境:

1.1 详解翻译环境: 

1.2 运行环境:

二、gcc实现程序的翻译与运行:

1. 预处理:

2. 编译: 

3. 汇编:

4. 链接:

4.1 动态链接与静态链接:

4.1.1 动态链接与静态链接的主要优缺点:

4.2 动态库与静态库:

三、g++实现程序的编译与运行: 


一、 背景知识:

1 程序的翻译环境与运行环境:

在ANSI C(C的一套标准)的任何一种实现中,存在两个不同的环境。

  • 第1种是翻译环境,在这个环境中源代码被转换为可执行的机器指令(二进制指令)。
  • 第2种是运行环境,它用于实际执行代码。

1.1 详解翻译环境: 

翻译环境是如何将源代码转换成可执行的机器指令的?

首先,翻译环境是由编译链接两个大的过程组成的,而编译又可以分解成:预处理(或预编译)、编译汇编三个过程。

一个C语言的项目中可能有多个 .c 文件一起构成,那多个 .c 文件如何生成可执行程序呢?

  • 多个 .c 文件单独经过编译器编译处理,生成对应的目标文件。
  • 注意:在Windows环境下,目标文件的后缀是 .obj ,Linux环境下,目标文件的后缀是 .o。
  •  多个目标文件和链接库一起经过链接器处理生成最终的可执行程序。
  • 链接库是指运行时库(它是支持程序运行的基本函数集合)或者第三方库。

如果再把编译器展开成3个过程,其过程将如下所示: 

 那预处理、编译、汇编与链接又分别在做什么呢?我们后面再讲。

1.2 运行环境:

程序执行的过程:

  1. 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序 的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。
  2. 程序的执行便开始。接着便调用main函数。
  3.  开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程 一直保留他们的值。
  4. 终止程序。正常终止main函数;也有可能是意外终止。 

二、gcc实现程序的翻译与运行:

基本格式:

gcc [选项] 要编译的文件 [选项] [目标文件]

我们这里以一个 test.c 为例。 

1. 预处理:

gcc -E test.c -o test.i

  • -E:指从现在开始,进行程序的翻译,做完预处理工作,就停下来。
  • -o:指明形成的临时文件名称。

当我们打开 test.i 文件时,由上图,我们会发现我们的程序内容变的很多了,其实这些都是我们的头文件里面的内容(如stdio.h),我们称这种操作叫做头文件包含。这些我们不关心,看后面的。 

由上图,我们发现 :我们的注释没有了,进行了条件编译,且进行了宏替换,但此时的代码还是C语言代码(干净的C语言)。

所以,在预处理阶段,gcc会对程序进行:

  • 头文件包含(头文件展开);
  • 去注释;
  • 宏替换;
  • 条件编译。

 头文件最大的意义:

  1. 帮助写代码,头文件分离开;
  2. 支持代码自动补齐。

2. 编译: 

gcc -S test.i -o test.s

  • -S:从现在开始,进行程序的翻译,做完编译工作,变成汇编代码后,就停下来。

 上图的代码是汇编代码

  • 在编译阶段中,gcc 首先要检查代码的规范性是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,gcc 把代码翻译成汇编语言

3. 汇编:

gcc -c test.s -o test.o

-c:从现在开始,进行程序的翻译,做完汇编工作后,变成可重定向目标二进制文件,就停下来。

 我们发现这个文件里面都是什么,怎么看不懂,这里我们需要执行如下命令:

od test.o(以二进制方式打开文件)

这样,我们就可以看出这是个二进制文件了。

  • 所以,汇编阶段是把编译阶段生成的“.s”文件转成二进制目标文件

但是,此时的目标文件还无法执行,因为还没有与C语言标准库链接。

因为目前上述的所有操作(预处理、编译、汇编)都是在编译我们自己写的代码,但是我们写的代码里面也调用了C语言标准库里面的函数(如printf、scanf等),我们没有实现这些函数。所以,此时程序执行这些函数时,需要跳转到库里面去执行库里面的代码。

4. 链接:

gcc test.o -o test

意思:链接形成可执行的二进制程序(库+自己写的代码)。

这里也可以直接 gcc test.o ,这样会自动生成一个 a.out 的可执行文件,我们这里只是自己设了一个文件名。

这里,我们就发现这个文件可以执行了。 

其实,上述这些命令,可以直接用下面的命令一步完成。

gcc 源文件名 -o 可执行文件名 

如:gcc test.c -o test 

4.1 动态链接与静态链接:

前提:链接的本质问题在于,我们调用库函数时,是如何和标准库关联起来的?

库函数跳转:我们在执行程序时,遇到的函数(如printf等)我们写的代码里没有具体实现方法,就要到库里面去执行库里面的代码以实现我们想要的功能(如打印),此时,就会在库里面直接执行我们想要的功能,然后把结果返回回来。最后又继续执行我们写的代码的后面的部分。

但是,程序是怎么知道库在哪里的呢?

这里就需要一个编译器内部的链接器来实现了,它会告诉我们库在哪里,每次我们需要调用库函数的时候就直接去库里面执行就行了,这就叫做和库建立链接。而每次我们执行到库函数时,都要和库建立链接,然后到库里面执行库函数,最后回来。这种链接方式,就叫做动态链接。这里的库是C标准库,它是一种动态库

链接的时候,不是与库建立关联,而是将我的程序内部要用的方法(函数)给我的程序拷贝一份,就是静态链接

4.1.1 动态链接与静态链接的主要优缺点:

1. 动态链接:

  • 优点:形成的可执行程序体积小(因此,可以节省资源 — 内存,磁盘,网络)。
  • 缺点:受到库升级或库删除的影响。

2. 静态链接:

  • 优点:不受库升级或库删除的影响。
  • 缺点:形成的可执行程序体积太大。

4.2 动态库与静态库:

静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。其后缀名一般为“.a”,windows下为“.lib”。

动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为“.so”,windows下为“.dll”。

  • 查看可执行程序是动态链接还是静态链接:

file 可执行文件 

  • 查看可执行程序依赖的动态库列表:

ldd 可执行文件 

1. Linux下库的命名规则: 

  • 动态库:以.so为后缀,以lib为前缀。libXXX.so
  • 静态库:以.a为后缀,以lib为前缀。libYYY.a

2. 如何知道一个库的名称:

 去掉前缀lib,去掉后缀.so/.a,剩下的就是库名称。

所以,Linux下形成的默认可执行程序,默认使用的是动态库 。

  • 实现静态链接的方法:

gcc 源文件名 -o 可执行文件名 -static

对于动态库,我们的系统一般都会自动携带(因为系统运行需要C动态库),而对于静态库,服务器可能就不会自动携带了。 

  • 安装静态库:

sudo yum install glibc-static

三、g++实现程序的编译与运行: 

g++实现程序的编译运行和gcc差不多,只需要将gcc改成g++即可。

g++ 源文件名 -o 可执行文件名

有可能服务器没有安装g++,可以先搜一下:

sudo yum list gcc-c++

然后,安装对应g++:

sudo yum install gcc-c++.x86_64

同样的,我们发现c++静态库没有安装。安装方法如下:

sudo yum install -y libstdc++-static 

一个小问题: 系统本身为了支持我们编程,给我们提供了什么?

  • 标准库的 .h(告诉我们怎么用)
  • 标准的动静态库(告诉我们有哪些已经实现了的方法,需要就直接调用)

所以,我们的程序通过包含头文件,链接别人的库,实现了:我的代码+库的代码=可执行程序

  • 23
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值