Linux开发工具 —— 编译器gcc/g++
四、Linux编译器 —— gcc/g++的使用
在此之前,我们可以使用命令 gcc -v 、g++ -v 查看 gcc、g++ 有没有安装:
1、背景知识
我们的 C 代码写完,要变成可执行程序需要四个阶段:
- 预处理
- 编译
- 汇编
- 链接
为啥不能直接将 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 [选项] 要编译的文件 [选项] [目标文件]
💦 预处理(进行宏替换等)
- 预处理功能主要包括头文件展开、条件编译、去掉注释、宏替换等
- 预处理指令是以 # 号开头的代码行
- 实例:gcc –E test.c –o test.i
选项 “-E” 该选项的作用是让 gcc 在预处理结束后停止编译过程,选项 “-o” 是指目标文件,“.i” 文件为已经经过预处理的干净的 C 原始程序
💨test.c
💨test.i
💦 编译(生成汇编)
-
在这个阶段中 gcc 首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,gcc 把代码翻译成汇编语言
-
用户可以使用 “-S” 选项来进行查看,该选项只进行编译而不进行汇编,其中生成的 “.s” 文件就是汇编语言
-
实例: gcc –S test.i -o test.s
💨test.s
虽然我们不学汇编,但至少得大概认识
💦 汇编(生成机器可识别代码)
- 汇编阶段是把编译阶段生成的 “.s” 文件转成目标文件
- 读者在此可使用选项 “-c” 就可以看到汇编代码已经转换为 “.o” 的二进制目标文件了
- 实例: gcc –c test.s -o test.o
💨test.o
💦 链接(生成可执行文件或库文件)
- 在成功编译之后就进入了链接阶段
- 实例: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” 了,而这也就是链接的作用。
💦 静态库和动态库
- 静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了,其后缀名一般为 “.a”。
- 动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为 “.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 倍以上。最后关于动静态库的更多细节在后面的学习中会慢慢摸索。
- 点赞
- 收藏
- 关注作者
评论(0)