Linux开发工具 —— 编译器gcc/g++

举报
跳动的bit 发表于 2022/06/21 13:55:26 2022/06/21
【摘要】 四、Linux编译器 —— gcc/g++的使用在此之前,我们可以使用命令 gcc -v 、g++ -v 查看 gcc、g++ 有没有安装: 1、背景知识我们的 C 代码写完,要变成可执行程序需要四个阶段:预处理编译汇编链接为啥不能直接将 C语言转成二进制 ❓它跟历史有关,早期编程可以认为是电路开关,往后是 0 1 指代,再往后有了汇编语言,在这时计算机就出现了编译器的概念了。比如时间回到...

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

在此之前,我们可以使用命令 gcc -v 、g++ -v 查看 gcc、g++ 有没有安装:
在这里插入图片描述
在这里插入图片描述

1、背景知识

我们的 C 代码写完,要变成可执行程序需要四个阶段:

  1. 预处理
  2. 编译
  3. 汇编
  4. 链接

为啥不能直接将 C语言转成二进制 ❓

它跟历史有关,早期编程可以认为是电路开关,往后是 0 1 指代,再往后有了汇编语言,在这时计算机就出现了编译器的概念了。比如时间回到 C语言诞生之时,现在有两种方案:要么把 C语言转成汇编,再转二进制;要么把 C语言直接转二进制。在计算机这门学科中永远是站在巨人的肩膀上学习的,把 C语言直接转二进制的成本一定会比把 C语言转成汇编,再转二进制的成本要高的。

注意 gcc 可以编译 C,但不能编译 C++;g++ 可以编译 C、C++。除了 C/C++,Linux 下几乎可以直接在命令行中执行大部分后端语言,比如 python、java 等。
在这里插入图片描述
当学完了 vim,那么以后的 C++ 代码、刷题都可以在 Linux 下进行操作,在刷题时,大部分的后端判题的系统都是 Linux,所以有时你会发现在做题时,在 VS 下是对的,提交却是错的,除了测试用例不全面,也有可以代码中包含了 windows 相关内容,如 windows.h 的头文件。

2、gcc如何完成

格式: gcc [选项] 要编译的文件 [选项] [目标文件]
在这里插入图片描述

💦 预处理(进行宏替换等)
  1. 预处理功能主要包括头文件展开、条件编译、去掉注释、宏替换等
  2. 预处理指令是以 # 号开头的代码行
  3. 实例:gcc –E test.c –o test.i
    选项 “-E” 该选项的作用是让 gcc 在预处理结束后停止编译过程,选项 “-o” 是指目标文件,“.i” 文件为已经经过预处理的干净的 C 原始程序

💨test.c

在这里插入图片描述

💨test.i
在这里插入图片描述

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

  2. 用户可以使用 “-S” 选项来进行查看,该选项只进行编译而不进行汇编,其中生成的 “.s” 文件就是汇编语言

  3. 实例: gcc –S test.i -o test.s

💨test.s

虽然我们不学汇编,但至少得大概认识
在这里插入图片描述

💦 汇编(生成机器可识别代码)
  1. 汇编阶段是把编译阶段生成的 “.s” 文件转成目标文件
  2. 读者在此可使用选项 “-c” 就可以看到汇编代码已经转换为 “.o” 的二进制目标文件了
  3. 实例: gcc –c test.s -o test.o

💨test.o
在这里插入图片描述

💦 链接(生成可执行文件或库文件)
  1. 在成功编译之后就进入了链接阶段
  2. 实例:gcc test.o -o mytest

💨执行 mytest

在这里插入图片描述

在本文中重点还要学习链接的过程,也就是下面动态库和静态库的概念。

💦 gcc选项
-E 只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面 
-S 编译到汇编语言,不进行汇编和链接 
-c 编译到目标代码
-o 文件输出到文件
-static 此选项对生成的文件采用静态链接
-g 生成调试信息,GNU 调试器可利用该信息
-shared 此选项将尽量使用动态库,所以生成的文件比较小,但是需要系统调动态库
-O0
-O1
-O2
-O3 编译器优化选项的 4 个级别,-O0 表示没有优化,-O1 为缺省值,-O3 优化级别最高
-w  不生成任何警告信息
-Wall 生成所有警告信息

在这里插入图片描述

💦 gcc选项记忆

为了方便记忆,我们可以看看键盘的左上角的 Esc 键,它刚好对应我们的 -E、-S、-c 选项;而生成的文件的后缀刚好对应镜象文件的后缀 .iso。

💦 函数库

我们的 C 程序中,并没有定义 “printf” 的函数实现,并且我们发现在预处理中包含的 “stdio.h” 中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实 “printf” 函数的呢 ❓

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

💦 静态库和动态库
  1. 静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了,其后缀名一般为 “.a”。
  2. 动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为 “.so”,如前面所述的 libc.so.6 就是动态库。gcc 在编译时默认使用动态库。完成了链接之后,gcc 就可以生成可执行文件,如后所示。 gcc hello.o –o hello。Linux 下编译好一个程序,默认情况下是属于动态链接形成的可执行程序
    在这里插入图片描述
    ldd 命令查看 mytest 所依赖的库;file 辨别 mytest 的类型。

📝小故事助理解

一般我们写的程序中,大部分都是动态链接,因为动态链接中不需要把库中的内容过多的拷贝,所以相对而言,它的编译效率较高,这是其一;其二,有时候我们下一些软件,比如 VS2017,它上面会有一些组件 C++、C# 等,这些组件并没有在你的硬盘中,而当你要安装时,它会帮你找到这些组件下载,这样有个好处就是咱需要啥就下啥,而不是直接一堆东西装在机器上,而自己需要使用的组件寥寥无几,所以这里就使用动态库的方式实现。一般在服务器上大部分也都是动态链接,不过有时需要将服务在很多机器上部署,那么单纯的动态链接有可能会出问题,因为动态链接是在程序运行之后才去对应加载到内存中,万一有一个库丢失了,那么程序就挂了。所以有时候一些程序它也会采用静态链接,它的好处就是它不依赖任何的动态库,坏处也很明显,效率低。比如静态链接的大小是动态链接的一百倍,要把静态链接这个程序下下来,只能全部下下来,而动态链接是边下边用。总的来说,它俩各有利弊。

比如你是一个新入学的大一新生,你在写作业,突然有了一个上网的需求,因为环境不熟悉,所以问了学长附近的网吧,随后就去玩了一个钟,接着回来继续写作业,这叫动态链接,它生成的可执行程序只包含你的代码及你要使用外部函数的链接。

后来大三了,要准备找工作了,家里给买了台电脑,当你正在学习时,你想上网,就不用去网吧了,只需要打开自己的电 脑玩个一个钟后又继续学习,也就是说静态链接,并没有和外界产生关联。所以静态链接本质就是将库里的代码拷贝到自己的可执行程序里。

📝小结

动态链接的生成的可执行程序的体积往往比较小,但是它依赖第三方库,有一定的风险,万一网吧被查封了。

静态链接虽然生成的可执行程序的体积较大,但是它不依赖第三方库,你有自己的电脑,网吧被查封了,照样可以玩,就是开学、放寒暑假不方便。

动态链接改静态链接 ❓

注意在新版本的 Linux 系统下一般都不会安装 libc.a,只会安装 libc.so,所以就使用不了 -static,解决方法就是安装 glibc-static

sudo yum install glibc-static

在这里插入图片描述
可以看到光是一个简单的程序,静态链接的大小就比动态链接高出 100 倍以上。最后关于动静态库的更多细节在后面的学习中会慢慢摸索。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。