👦个人主页:Weraphael
✍🏻作者简介:目前正在学习c++
和Linux
还有算法
✈️专栏:Linux
🐋 希望大家多多支持,咱一起进步!😁
如果文章有啥瑕疵,希望大佬指点一二
如果文章对你有帮助的话
欢迎 评论💬 点赞👍🏻 收藏 📂 加关注😍
目录
- 前言
- 一、 Linux下编译C/C++代码
- 二、gcc/g++是如何完成代码编译
- 2.1 预处理
- 2.2 编译
- 2.3 汇编
- 2.4 链接
- 三、库
- 3.1 函数库的概念
- 3.2 动态库
- 3.3 静态库
- 3.4 小结
前言
大家的云服务器可能没有gcc
或者g++
指令,如果没有可以分别执行以下指令
-
sudo yum install -y gcc
-gcc
指令安装 -
sudo yum install -y gcc-c++
g++
指令安装
注意:如果sudo
指令有问题的,建议先看看这篇博客 -> 点击跳转
一、 Linux下编译C/C++代码
gcc [.c文件]
执行完gcc [.c文件]
后,默认会生成可执行文件 a.out
(前提是代码没有语法错误)
当然也有人想要为这个可执行文件起个名字,那么就要通过 -o
选项 来实现
gcc [.c文件] -o [新名字]
注意:g++
也可以通过 -o
选项生成指定文件。其指令基本都是一样的
二、gcc/g++是如何完成代码编译
由于gcc
和g++
只有编译文件类型不同,其他大差不差,因此以下就以gcc
为例
2.1 预处理
预处理会进行以下操作:
- 去注释
- 头文件展开
- 条件编译
- 宏替换
我们可以直接通过gcc
中的-E
选项,来查看预处理的现象。注意:预处理后的文件后缀为.i
,此时仍然是C语言代码。目的是生成一个干净的C代码程序
gcc -E [.c文件] -o [.i文件]
接下来我们可以打开预处理阶段的文件test.i
来看看里面的代码
大家有没有想过这样一个问题:头文件展开就是将头文件的内容给拷贝过来,那我们怎么知道头文件在哪?
- 其实标准C库头文件(如
stdio.h
、stdlib.h
等)通常位于/usr/include
目录下。
- 当然了,标准
C++
库头文件(如iostream
、vector
等)通常位于/usr/include/c++
目录下,该目录下还会有不同版本的子目录,对应不同的C++
标准和编译器版本。
2.2 编译
- 编译阶段会进行:语法分析、词法分析、语义分析、符号汇总等,然后将合法的代码转为汇编代码。
编译阶段比较重要的一步就是符号汇总,它会各种符号汇总起来,形成符号表,符号表用于各种函数间的相互调用
我们可以直接通过gcc
中的-S
选项,来查看编译阶段的现象。注意:编译阶段的文件后缀为.s
,此时 .s
文件里是源文件的汇编代码。
gcc -S [.c文件] -o [重命名为.s文件]
// 注意[.c文件]也可以替换成[.i]文件
接下来,我们可以打开test.s
查看汇编
2.3 汇编
- 主要任务是将汇编代码转为二进制(转为计算机懂的语言),并生成符号表
【补充】 什么是符号表
这个东西相当于函数独一无二的地址
C
语言的符号是 _函数名
C++
更详细一些,通常为 _Z函数名长度+函数名+ 形参类型的首字母
我们可以直接通过gcc
中的-o
选项,来查看汇编阶段的现象。注意:汇编文件后缀为.o
,此时 .o
文件将源文件转化为二进制文件。
gcc -c [.c文件] -o [重命名为.o文件]// 或者可以从[.s文件]开始编译
gcc -c [.s文件] -o [重命名为.o文件]
我们可以打开test.o
来【欣赏】二进制文件
我们发现是一堆乱码,这是一个正常的现象,因为test.o
本身是二进制文件,其,而vim
是文本编辑器,自然而然就看不懂
但是我们可以使用工具 readelf
:是一个用于查看和分析二进制可执行文件格式elf
的工具。
readelf -a [二进制文件]
【以下只截取了一部分】
注意:二进制文件不可执行,需要通过链接才能执行。
2.4 链接
- 将可重定位目标的二进制文件(或称目标文件
.o
),和库进行链接形成可执行文件。
// 两种写法
gcc [.c文件] -o [重命名可执行文件]
gcc [.o文件] -o [重命名可执行文件]
上面说了,目标文件需要和库进行链接,因此接下来我们来谈谈库。
三、库
3.1 函数库的概念
C
程序中,并没有定义printf
函数(我们只是调用函数),且在预编译中包含的stdio.h
中也只有该函数的声明,而没有定义函数的实现。那么,是在哪里实现printf
函数的呢?
在Linux
系统中,它把C
语言函数实现都被放到名为libc.so
的库文件中去了(路径:/usr/lib64/libc.so
)。
没有特别指定时,gcc
会到系统默认的搜索路径/usr/lib
下进行查找,也就是链接到libc.so
库函数中去。
如上图所示,在Linux
中,C语言函数库是一个文件,以.so
为后缀的称为动态库;以.a
为后缀的称为静态库。
另外,库是有自己的命名规则的。以lib
为前缀,name
为中缀,.so.版本
为后缀。而库真正的名字只有中缀那一块,这也就为什么libc.so
称为C语言函数库的原因了。
注意:我们现在的机器上,默认只会安装动态库,静态库是默认没有安装的
【总结】
-
函数的实现就是在库当中的
-
库其实就是把源文件(
.c
文件),经过一定的翻译,然后打包,只给你提供一个文件(库文件)即可,不用给你提供给太多的源文件,也可以达到隐藏源文件的目的 -
头文件提供方法的声明, 库文件提供方法的实现 + 我们自己的代码 == 可执行程序
-
因此。库的作用:不用做很多重复的工作。
3.2 动态库
动态库即通过动态链接的库,动态库又称共享库,因为动态库中的内容是被所有程序共享的,简言之动态库中的代码只需要存在一份,程序需要使用时,直接通过对应位置调用就行了
在Linux
中默认使用 动态链接 的方式,我们可以通过 指令ldd
来查看文件的链接情况
我们还可以通过 file
命令查看文件详细信息
3.3 静态库
静态库采用静态链接的方式;静态链接不同与动态链接共享的方式,如果程序调用静态库 ,会将自己所需要的代码拷贝至程序中 ,完成拷贝后,后续不需要再调用静态库。
如果想采用静态链接链接的方式编译程序,需要在编译时加上-static
选项
gcc [源文件] -o [自己取] -static
而我们开头说了,我们的云服务器默认是没有静态库的,没有的可以通过sudo yum install -y glibc-static
下载。还有我们也可以装一下c++的静态库sudo yum install -y libstdc++-static
当然,我们也可以通过ldd
和file
指令查看链接情况
由于静态链接是直接将需要的代码拷贝到程序中,因此最终生成的文件会变大,比较占空间
3.4 小结
【动态库】
-
优点
- 动态库因为是共享库,有效的节省资源(节省磁盘空间,内存空间,网络空间等)
-
缺点
- 动态库一旦缺失,导致各个程序都无法运行
静态库
- 优点
- 程序运行无需依赖库,可以独立运行
- 缺点
- 体积大,比较消耗资源,浪费空间