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

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

目录

该篇博客需要使用vim编译器要是对其不熟悉可以查看该篇博客vim的使用

一.背景

一个源文件生成可执行文件需要经过程序的翻译环境将程序的文本文件翻译为机器可以识别的二进制文件而翻译环境可以分为以下四个步骤

  1. 预处理头文件替换、条件编译、宏替换、去注释等
  2. 编译生成汇编语言
  3. 汇编生成机器可识别二进制文件不可被执行bin.obj
  4. 链接将我们自己形成的.obj文件和库文件进行某种合并生成可执行文件

具体可以看这篇博客程序环境

gcc/g++可以帮我们在LInux下生成C/C++的可执行文件并通过选项分别执行到翻译环境的四个不同的步骤。
gcc编译C语言代码g++编译C++代码两者的使用方法相同以下以gcc为例。

二.gcc如何生成

格式gcc [选项] 要编译的文件 [选项] [目标文件]
我们先创建一个hello.c的测试文件并使用vim编辑器编写代码

[YX@VM-16-5-centos lesson7]$ touch hello.c    //创建hello.c文件
[YX@VM-16-5-centos lesson7]$ vim hello.c      //使用vim编译器编译代码

在这里插入图片描述
我们可以直接使用gcc生成该测试文件的可执行文件
在这里插入图片描述
接着使用./a.out执行该可执行文件就可以得到我们编写代码的结果
在这里插入图片描述
但当我们想要的到它在翻译环境下四个不同阶段生成的文件时我们该如何执行

1.预处理进行宏替换

  • 预处理功能主要包括宏替换文件包含条件编译去注释等。
  • 预处理指令是以#开头的代码行。
  • 实例gcc -E hello.c该指令为直接得到预处理后的内容将其打印在显示器上不方便查看不做演示
  • 实例gcc -E hello.c -o hello.i将预处理后的内容放入.i文件中
  • 选项"-E"该选项的作用是让 gcc 在预处理结束后停止编译过程。
  • 选项"-o"是指将前面执行的结果放入后面的文件”.i“文件为已经过预处理的C原始程序。
  • 注意文件名可以随便起这个不影响但最好遵循规则将其写为.i文件下面的文件同理

按照上面的指令我们执行并生成预处理后的文件
在这里插入图片描述
注意.i文件不是可执行文件我们不能使用./hello.i执行该文件但可以通过vim编辑器查看

最终可以看到该文件的内容

在这里插入图片描述
我们已经知道了如何查看预处理生成的文件但上面的案例并没有完全展示预处理作用这里我们先将测试文件修改后在生成.i文件查看其结果就可以明显的感受到预处理的作用如下

在这里插入图片描述

  • 对于.i文件我们可以看到它有860行而.c文件只有24行.i文件中多余的代码就是被包含头文件的代码

2.编译(生成汇编)

  • 在这个阶段中gcc首先要检查代码的规范性、是否有语法错误等以确定代码实际要的工作在检查无误后gcc把代码翻译成汇编语言。
  • 用户可以使用”-S“选项来进行查看该选项只进行编译而不进行汇编生成汇编代码。
  • 实例gcc -S hello.c直接将.c文件生成汇编后的内容显示在显示器上重新经历预处理不建议这样使用
  • 实例gcc -S hello.i -o hello.s该指令是将.i文件生成.s文件
  • 实例gcc -S hello.c -o hello.s也可以直接将.c文件生成.s文件重新经历预处理)
  • ”.s“文件为已经过编译的C原始程序

我们这里直接将.i文件生成.s文件

在这里插入图片描述

打开后可以清晰的看到它生成的汇编代码

在这里插入图片描述

3.汇编(生成机器可识别代码)

  • 汇编阶段是把编译阶段生成的”.s“文件转成目标文件。
  • 在此可以使用选项”-c“就可看到汇编代码已转化为”.o“的二进制目标代码了
  • 实例gcc -c hello.c同上直接生成汇编后的数据显示在显示器
  • 实例gcc -c hello.c -o hello.o同上从.c文件开始重新经过预处理、编译直到汇编
  • 实例gcc -c hello.s -o hello.o由编译后的文件生成汇编后的文件

我们这里将.s文件生成汇编后的.o文件

在这里插入图片描述
打开后看到的是机器可以别的代码我们看不懂的

在这里插入图片描述

4.链接(生成可执行文件或库文件)

  • 在成功编译之后就进入了链接阶段。
  • 实例gcc hello.c -o hello从.c文件起经过翻译环境直到链接
  • 实例gcc hello.o -o hello由汇编生成的.o文件生成链接后的文件
  • 链接后的文件是可执行文件可以直接执行./hello得到程序结果

我们使用.o文件生成链接后的文件

在这里插入图片描述

查看可执行文件中的内容

在这里插入图片描述
为机器识别的代码

问题

既然汇编和链接后生成的文件内容都是机器识别的代码那么汇编后生成的文件是否可以执行

我们先对其进行执行操作
在这里插入图片描述
发现该文件没有可执行权限给它加上在执行

在这里插入图片描述

结论 即便一个文件具有可执行程序它不是可执行文件就没有办法执行同时证明.o文件无法执行

使用chmod -x hello.o取消它的可执行权限。

三.函数库

在链接阶段我们链接了库文件那什么是库文件呢这就要细细说来。

我们写的C/C++程序在编译器上之所以可以运行是因为我们的编译器将程序经过翻译环境变成可执行程序最终输出结果。

我们写的程序中除了我们自己编写的代码还要调用头文件中提供的函数如我们经常使用的printf、scanf函数就是在头文件stdio.h中声明的这些头文件在Linux下存放在/usr/include目录及子目录下

在这里插入图片描述
既然这些函数的声明是放在头文件中那他们在哪里定义

我们知道翻译环境分为四个步骤最后一个步骤链接的作用就是使汇编后生成的文件和库文件进行结合而头文件声明的函数就存放在库文件中。

Linux系统下会把这些函数实现都做到名为libc.so.6的库文件中去了在没有特别指点时gcc会到系统默认的搜索路径“/usr/lib”下进行查找也就是链接到libc.so.6库函数中去。这样就能实现函数“printf”或其他函数了而这也就是链接的作用。

在这里插入图片描述
在比如我们看看上面写的测试文件hello.c的可执行文件a.out看一下它链接的库文件这里我们需要使用ldd指令查看。

在这里插入图片描述
实际上在我们安装vs2019、vs2022这些编译器的时候最重要的一个工作就是帮我们下载并安装语言的头文件和库文件。

所以我们可以得出结论函数库本身就是文件用来定义和存放函数。

1.函数库的分类

函数库分为两种静态库和动态库它们的区别如下

(1)动态库

命名libXXXXX.so

其中XXXXX可以使任意名字如上面所使用的库文件libc.so.6就是一个动态库。去掉前缀lib和后缀.so剩余c表面它是由c语言编写的库而剩余的.6表示版本号。

动态库的作用动态链接库文件在链接阶段拷贝动态库中我们需要的代码的地址到我们自己的可执行程序中的相关位置在运行程序时通过地址调用动态库中函数保证代码的正常运行。

我们编写的代码在链接时如果没有指定默认链接的都是动态库。

(2)静态库

命名libXXXXX.a

如上lib为前缀.a为后缀中间可以是任意名

静态库的作用静态链接库文件在编译链接时把静态库中我们所需要的代码拷贝到可执行文件中因此生成的文件比较大但在运行时也就不在需要库文件了。

比如我们写程序时printf函数可能要出现不止一次那么每个printf函数都会被替换为静态库中对应的代码这样就使文件变大多次替换的printf函数浪费了空间。

静态链接需要我们自己链接链接方法如下

  1. 我们可以通过file指令查看可执行文件链接的是动态库或静态库或是使用ldd指令查看链接库的命名根据命名判断
    在这里插入图片描述

  2. 通过指令gcc hello.c -o hello-static -static使生成的hello-static可执行文件静态链接在这里插入图片描述

    • 静态链接和动态链接后的文件相差10倍左右十分正常
  3. 我使用的是云服务器默认只有动态库我们需要自己手动安装静态库才可以使用方法如下

     sudo yum install -y glibc-static   //在普通用户下安装c语言静态库
    yum install -y glibc-static        //在root用户下安装c语言静态库
    
    sudo yum install -y glibc-static libstdc++-static    //在普通用户下安装c++静态库
    yum install -y glibc-static libstdc++-static    //在root用户下安装c++静态库
    

2.区别

动态库通过程序中动态库代码对应的地址调用动态库中对应的函数完成代码的执行如果动态库突然消失可执行程序将无法执行。

静态库通过函数名生成的地址在库中找到对应的函数将其拷贝到可执行文件中在文件中调用这使得可执行文件即使没有静态库也可以执行代码但随着拷贝可执行文件也会变大。

3.拓展

我们在Linux中使用的指令都是使用c语言编写的我们可以查看不同指令的链接库看是否使用c语言编写如下

查看ls指令的库文件

在这里插入图片描述
查看which指令的库文件

在这里插入图片描述

所以指令就是程序。

  • 关于函数库的知识该篇博客就只有这些了更多的内容会在基础I/O中讲解。

四.记忆

1.选项

我们在进行翻译环境的四个步骤中都使用了-o选项而链接只使用了-o剩余三个步骤我们可以通过键盘左上角的Esc按钮来记忆。

预处理——-E编译——-S汇编——c

我们要是忘记了只需要看键盘左上角的Esc按钮但注意ES是大写的。

2.后缀

链接生成的文件没有后缀不需要记忆其余三个按顺序刚好对应镜像文件的后缀.iso

五.gcc选项

gcc的选项中最常用的就是上面翻译环境四个步骤的各个选项将其汇总并和其他的一些选项一起放在这里方便大家查看。

  • -E 只激活预处理这个不生产文件需要把它重定向到一个输出文件里面。
  • -S 编译到汇编语言不进行汇编和链接。
  • -c 编译到目标代码。
  • -o 文件输出到文件。
  • -static 此选项对生成的文件采用静态链接。
  • -g 生成调试信息。GNU 调试器可利用该消息。
  • -shared 此选项将尽量使用动态库所以生成文件比较小但是需要系统由动态库。
  • -O 0
  • -O 1
  • -O 2
  • -O 3 编译器的优化选项的4各级别-O0表示没有优化-O1为缺省值-O3优化级别最高。
  • -w 不生成如何警告信息。
  • -Wall 生成所有警告信息。
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: linux