Linux 系统 C / C++ 代码编译器 —— gcc / g++

devtools/2024/9/25 10:36:09/

Linux 系统 C / C++ 代码编译器 —— gcc / g++

  • 编译器使用
    • 可能存在的版本问题
      • 解决
    • 简单使用
      • 指定生成的可执行程序文件名
      • 匹配使用
  • 程序的翻译过程
    • 预处理
      • 实操
      • 条件编译
    • 编译
      • 实操
    • 汇编
      • 实操
    • 链接
      • 实操
    • 为什么会有这些过程

编译器使用

可能存在的版本问题

gcc / g++ 是开发必备的编译器,由于前面学习过 yum 这里对于安装就不说了(网上一大把),但如果图省事,是 yum 默认安装的 gcc / g++ ,版本会比较低,如下:

[root@localhost compiler]$ g++ --version
g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-44)
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.[root@localhost compiler]$ gcc --version
gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-44)
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

解决

可以 添加选项支持高版本

  • 对于 gcc 来说,可添加 -std=c99 选项支持高级特性(可能会有问题,后面说)
  • 对于 g++ 来说,可添加 -std=c++11 选项支持高级特性

初阶使用 并不建议 直接安装高版本编译器,因为 可以通过是否添加选项来细致理解语言不同版本的差别和特性

简单使用

gccg++ 的用法几乎一样,简单的使用就是直接跟上需要编译的文件名就行:

gcc code.c
g++ code.cc

如果没有编译和链接报错,就会生成一个可执行的文件;
但有问题,新生成的可执行程序文件名是 a.out (可以通过 ll 指令查看),在当前目录下,执行如下指令即可运行:

./a.out

指定生成的可执行程序文件名

如果我要指定生成的可执行程序文件名呢?如下添加 -o 选项即可( gcc 也是这样使用):

g++ code.cc -o my.exe

-o my.exe 选项跟在 g++ 后面也行,这里的 my.exe 就是被指定的文件名,执行它即可运行:

./my.exe

匹配使用

  • 编译器匹配:
    • gcc 只能编译 C 语言代码
    • g++ 既可以编译 C 语言代码,也可以编译 C++ 代码
  • 文件名匹配
    • C 语言代码只能用 .c 后缀
    • C++ 可使用 .cc.cpp.cxx 后缀

文件名要匹配不是给系统识别的( Linux 系统不以后缀标识文件类型),而是编译器 gcc / g++ 要匹配,不然会报如下错(以文件 code.xxx 为例):

code.xxx: file not recognized: File format not recognized
collect2: error: ld returned 1 exit status

程序的翻译过程

首先要知道程序翻译过程中的所有步骤,分别是:

  • 预处理
  • 编译
  • 汇编
  • 链接

预处理

这是对一个代码文件的预处理,包括 宏替换去注释头文件展开条件编译 等等,完成这些任务后,生成的输出是一个没有宏定义、条件指令或者包含文件的 “干净” 的 C 语言源文件

我们可以通过写一篇拥有 注释头文件 的 C 语言程序在 Linux 上面跑一下,利用 gcc 的选项来使翻译过程停在我们想要的步骤上

实操

那么假如现在有一篇 code.c 源文件,内容如下:


#include <stdio.h>
#define M 100int main()
{printf("C\n");for (int i = 0; i < 10; ++i){printf("Hello Linux!!! M: %d\n", M);}printf("Chehe\n");//printf("Chehe\n");//printf("Chehe\n");//printf("Chehe\n");//printf("Chehe\n");//printf("Chehe\n");//printf("Chehe\n");//printf("Chehe\n");//printf("Chehe\n");//printf("Chehe\n");printf("Chehe\n");return 0;
}

可以通过如下指令进行 预处理 ,且停在 编译 之前:

gcc -E code.c -o code.i

-o 选项是对 预处理 后的文件进行命名(这里是 code.i ),不然会直接打印在命令行上,不好查看
-E 是指:从当前文件状态开始翻译,只进行 预处理,完成就停下

所以上述指令就是进行上述过程,将结果放进 code.i

这里就不展示结果长什么样,但会有 800 多行,里面大部分都是头文件的展开内容,实际上就是直接拷贝的 <stdio.h> 里的内容,只是会去掉 注释 ,相应 条件编译 之类的内容;

剩下的就会发现,上面所说的功能 预处理 都已完成

条件编译

大多数应该都知道或者了解,但 条件编译 未必清晰明了

有时候在下载相关软件的时候会发现有免费版,社区版,专业版之类的,常识来说这些对应的版本就是存在功能阉割, 专业版的功能更加多元强大 ,那这里有个问题:不同版本之间是不同的代码吗?

并不是,因为就算是功能阉割,但绝大多数功能都是具备的,如果要维护好几份不同的代码,岂不是麻烦(如果在共同功能上有 Bug ,那是否要修改好几次,测试好几次呢)?我们可以利用 条件编译

首先要记住 gcc 可以 对代码进行动态裁剪 ,绝对是可以的!!!

我们来验证一下:
写上这样一份代码并编译:

// proj.c 文件
#include <stdio.h>
void Diff_Vers()
{
#ifdef V1printf("功能1\n");printf("功能2\n");
#elif V2printf("功能1\n");printf("功能2\n");printf("功能3\n");printf("功能4\n");
#elseprintf("功能1\n");printf("功能2\n");printf("功能3\n");printf("功能4\n");printf("功能5\n");printf("功能6\n");
#endif
}
int main()
{Diff_Vers();return 0;
}
gcc proj.c -o proj.exe

此时运行 proj.c 文件,显示出来的就是 6 个功能都有,可利用指令 gcc -E proj.c -o proj.i 来查看编译器对条件编译的动态裁剪情况

上面没有定义 V1V2 ,就动态裁剪了 V1V2 对应的代码

命令行宏定义 V1 ,则 只保留 V1

gcc -DV1=1 proj.c -o proj.exe

命令行宏定义 V2 ,则 只保留 V2

gcc -DV2=1 proj.c -o proj.exe

这里的 -D 选项就是为我们定义了 V1V2 的宏定义,实现代码的动态裁剪

编译

记住其 核心作用 就是帮我们 做语法检查

我们平时的程序报错无非就三种:

  • 编译时语法报错 ,这就是很简单也很明了的错误,遵循相关语法即可,编译器甚至会告诉你报错在第几行
  • 链接式报错
  • 运行时报错

编译 会在 预处理 后的源文件的基础上,进行词法语法分析,主要工作就是 把 C 语言变成汇编语言

实操

可以通过如下指令进行 编译 ,且停在 汇编 之前:

gcc -S code.i -o code.s -std=c99

-S 是指:从当前文件状态开始翻译, 编译 完成就停下

这时可以查看 code.s 汇编代码文件

汇编

汇编就很恶心了,目前来说比较陌生,但其核心工作就是 把汇编语言翻译成为二进制目标文件

实操

可以通过如下指令进行 汇编 ,且停在 链接 之前:

gcc -c code.s -o code.o

-S 是指:从当前文件状态开始翻译, 汇编 完成就停下

这时可以查看 code.o 二进制目标文件 ,此时文件内容已经乱码,面目全非

链接

虽说二进制文件已经和机器语言很接近了,但还是不能直接交给底层去运行,因为少 链接 ,什么意思呢?

平时写代码时,我们会自己把所有的实现细节都自己写出来吗?不会,而是复用人家的第三方库对吧(提高开发效率),那库里的东西你凭什么可以直接拿过来用呢?又不是你写的,所以就需要 将人家的 C 标准库(以头文件加库的形式提供给你)文件(或者是更多其他文件)和自己的二进制目标文件进行链接,形成可执行程序

而链接的库分为 静态库动态库 ,下面是 后缀名

所在系统静态库动态库
Linux.a.so
Windows.lib.dll

当然分别对应的链接方式就是 静态链接动态链接

动态库 也是 共享动态库 ,当你链接这个库时,会将库中相关代码加载进内存,当未来有其他程序也要使用这个库时,可以直接跳过来执行同一份库代码,所以资源占用不多,但一旦 动态库 缺失,后果将因为找不到库而 链接 报错(第一次运行),程序崩溃(后来因某种原因缺失 动态库

静态库 对应的 静态链接,是在编译的时候,把库的方法拷贝进自己的可执行程序中,未来执行时,就算没有这个静态库,也丝毫不影响程序运行,不用关心库问题,可移植性高,跨平台能力强;但如果其他程序都需要此库,将会大量加载相同代码,导致资源占用多,速度降低,性能有问题

你可以通过指令 ldd 来查看一个可执行程序依赖的库:

ldd 可执行程序名
ldd /usr/bin/ls
ldd /usr/bin/pwd

实操

可以通过如下指令进行 链接

gcc code.o -o code.exe

此时已经可以运行 code.exe 文件了

为什么会有这些过程

因为历史选择

在还没有 C 语言但有汇编语言的时候,汇编代码需要编译器吗?当然需要!

那 C 语言编译器还需要重新实现一遍如何把汇编语言变成二进制文件吗?自然就不需要了,因为已经有汇编代码的编译器,所以 C 语言编译器真正需要做的就是如何把 C 语言变成汇编代码,即 编译 这一步,而后面已经有前人的努力,所以就有这样一步一步的翻译过程,效率也最高


http://www.ppmy.cn/devtools/88534.html

相关文章

格雷厄姆的《聪明的投资者》被誉为“投资圣经”

本杰明格雷厄姆的《聪明的投资者》&#xff08;The Intelligent Investor: A Book of Practical Counsel&#xff09;是投资领域的一部经典之作&#xff0c;被誉为“投资圣经”。以下是对该书的详细解析&#xff1a; 一、书籍基本信息 书名&#xff1a;《聪明的投资者》&…

最后一块石头的重量(超级妙的背包问题)

1049. 最后一块石头的重量 II 有一堆石头&#xff0c;用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。 每一回合&#xff0c;从中选出任意两块石头&#xff0c;然后将它们一起粉碎。假设石头的重量分别为 x 和 y&#xff0c;且 x < y。那么粉碎的可能结果…

[CISCN2019 华北赛区 Day1 Web1]Dropbox 1

目录 题目分析功能测试代码读取index.phpdownload.phpdelete.phpclass.php 关键代码审计user类FileList类File类思路 解题步骤php脚本解题 题目分析 功能测试 注册登录后来到上传文件界面&#xff0c;通过改后缀&#xff0c;改文件头&#xff0c;改content-type&#xff0c;上…

设计模式-创建型模式-单例设计模式

创建型模式提供创建对象的机制&#xff0c;能够提升已有代码的灵活性和复用性&#xff1b; 常用的有&#xff1a;单例模式、工厂模式、建造设模式&#xff1b;不常用的&#xff1a;原型模式&#xff1b; 1.概述 单例模式是最简单的模式之一&#xff0c;其保证了某个类在运行期…

32.x86游戏实战-使用物品call

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 工具下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1rEEJnt85npn7N38Ai0_F2Q?pwd6tw3 提…

面试题-每日5道

26.在 Queue 中 poll()和 remove()有什么区别? 相同点&#xff1a;都是删除第一个元素并返回。 不同点&#xff1a;如果没有元素poll()会返回null,而remove()会抛出NoSuchElementException异常 27.哪些集合类是线程安全的&#xff1f; Vector,Stock,Hashtable都是线程安全的&a…

了解三大无线通信技术(Wifi、蓝牙、NFC)

1.WIFI Wi-Fi&#xff08;Wireless Fidelity&#xff09;&#xff0c;又称作“移动热点”&#xff0c;是当今使用最广的一种无线网络传输技术。Wi-Fi技术是把有线网络信号转换成无线信号&#xff0c;形成无线局域网&#xff0c;将局域网内的设备联网。比如我们通过一个无线路由…

1平方 1.5平方 2.5平方等 电线线缆功率

抖音号&#xff1a;dilo_Abel 首先电工口中的1平方线是指横截面积是1平方毫米的电线 以下是不同平方毫米截面积的铜芯电线对应的负荷功率、电流、电线直径和AWG线号的表格&#xff1a; 电线截面积 (平方毫米)电线直径 (毫米)AWG (美国线规)单相220V电路 (最大电流/功率)三相…