4 gcc
约 4064 字大约 14 分钟
2025-04-05
gcc [-E\S\c] 源文件名+后缀 -o 目标文件名+后缀
-E:将c语言文件预处理生成.i文件 -S:将处理好的c语言文件汇编生成.s文件 -c:将汇编文件进行编译成二进制.o文件
gcc .o文件 -o 目标文件名
将.o文件进行链接生成可执行文件 也可以直接使用这个命令从c语言文件直接生成可执行文件
-o:指定生成的文件名字,如果不使用该选项不指定名字则默认生成文件名为a
编译选项 | 作用 |
---|---|
-E | 预处理指定的源文件,不进行编译 |
-S | 编译指定的源文件,但是不进行汇编 |
-c | 编译、汇编指定的源文件,但是不进行链接 |
-o [file1] [file2] / [file2] -o [file1] | 将文件 file2 编译成文件 file1 |
-I directory (大写的i) | 指定 include 包含文件的搜索目录 |
-g | 在编译的时候,生成调试信息,该程序可以被调试器调试 |
-D | 在程序编译的时候,指定一个宏 |
-w | 不生成任何警告信息, 不建议使用, 有些时候警告就是错误 |
-Wall | 生成所有警告信息 |
-On | n的取值范围:0~3。编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高 |
-l | 在程序编译的时候,指定使用的库 |
-L | 指定编译的时候,搜索的库的路径。 |
-fPIC/fpic | 生成与位置无关的代码 |
-shared | 生成共享目标文件。通常用在建立共享库时 |
-std | 指定C标准,如:-std=c99,gcc默认的方言是GNU C |
-fdiagnostics-color-always | 始终强制染色诊断信息 |
# -
#紧贴着写和用空格隔开都是可以的
gcc test.c -Iinclde
gcc test.c -I inclde
#在程序中使用到一个宏却没有定义的时候,常用于日志输出
gcc test.c -D DEBUG
gcc与g++
gcc和g++都可以编译C++程序,如果C++程序使用C++标准库函数(或类)gcc编译出错主要是在链接阶段gcc没有链接到C++库
- 在代码编译阶段(第二个阶段):
- 后缀为
.c
的,gcc 把它当作是C程序,而 g++ 当作是 C++ 程序 - 后缀为
.cpp
的,两者都会认为是 C++ 程序,C++ 的语法规则更加严谨一些 - g++会调用gcc,对于C++代码,两者是等价的, 也就是说 gcc 和 g++ 都可以编译 C/C++代码
- 后缀为
- 在链接阶段(最后一个阶段):
- gcc 和 g++ 都可以自动链接到标准C库
- g++ 可以自动链接到标准C++库, gcc如果要链接到标准C++库需要加参数
-lstdc++
- 关于
__cplusplus
宏的定义- g++ 会自动定义
__cplusplus
宏,但是这个不影响它去编译C程序 - gcc 需要根据文件后缀判断是否需要定义
__cplusplus
宏 (规则参考第一条)
- g++ 会自动定义
[!NOTE] Title
__cpluscplus
宏表示C++标准
静态库和动态库
要生成动态库和静态库的c语言源文件不能有main函数
静态库
在Linux中静态库由程序 ar
生成
- 在Linux中静态库以
lib
作为前缀, 以.a
作为后缀, 中间是库的名字自己指定即可, 即:libxxx.a
- 在Windows中静态库一般以
lib
作为前缀, 以lib
作为后缀, 中间是库的名字需要自己指定, 即:libxxx.lib
生成静态库,需要先对源文件进行汇编操作 (使用参数 -c
) 得到二进制格式的目标文件 (.o 格式
), 然后在通过 ar
工具将目标文件打包就可以得到静态库文件了 (libxxx.a
)。
ar [rcs] 静态库的名字(libxxx.a) 源文件(*.o)
参数c
:创建一个库,不管库是否存在,都将创建。参数s
:创建目标文件索引,这在创建较大的库时能加快时间。参数r
:在库中插入模块(替换)。默认新的成员添加在库的结尾处,如果模块名已经在库中存在,则替换同名的模块。
静态库的发布与使用
- 提供头文件 .h
- 提供制作出来的静态库 libxxx.a 若程序使用了静态库提供的方法在gcc编译时需要使用
-l
或-L
指定出来使用的库,如果不指定会报错未定义 在指定静态库时需要“掐头去尾”,如:静态库名为libxx.a 在使用-l指定时参数就是-l xx
参数与参之间可以有空格也可以没有
动态库
- 在Linux中动态库以
lib
作为前缀, 以.so
作为后缀, 中间是库的名字自己指定即可, 即:libxxx.so
- 在Windows中动态库一般以
lib
作为前缀, 以dll
作为后缀, 中间是库的名字需要自己指定, 即:libxxx.dll
在windows中使用vs生成的动态库是两个文件:.dll文件和.lib文件,这两个加起来才是完整的动态库。使用其他工具生成的动态库只有一个.dll文件
生成动态链接库是直接使用gcc
命令并且需要添加-fPIC(-fpic)
以及-shared
参数。
[!tip] 和位置无关 进程是磁盘上加载的应用程序,只要运行进程就会得到一个虚拟地址空间,在虚拟地址空间中加载一些代码。如果是静态库,就会打包到可执行程序中,因此静态库是放在代码区;如果使用的是动态库,动态库会放到动态库加载区,且动态库是在程序运行时用到了动态库里面的方法才把动态库加载到动态库加载区。因此在不同的进程中要调用动态库文件对应的位置都是不一样的。使用-fpic之后调用库函数对应的代码在虚拟地址空间中是相对地址。如果是静态库是绝对地址
-fPIC 或 -fpic
参数的作用是使得 gcc 生成的代码是与位置无关的,也就是使用相对位置。
-shared参数
的作用是告诉编译器生成一个动态链接库。
gcc -shared 目标文件(*.o) -o 动态库(libxxx.so)
-shared放在哪都行
动态库的发布和使用同样的也需要和.h头文件一起
动态库无法加载问题
动态库在使用时gcc通过指定的动态库信息生成了可执行程序, 但是可执行程序运行却提示无法加载到动态库
。
-在gcc命令中虽然指定了库路径(使用参数 -L
), 但是这个路径并没有记录到可执行程序中,只是检查了这个路径下的库文件是否存在。 同样对应的动态库文件也没有被打包到可执行程序中,只是在可执行程序中记录了库的名字。 可执行程序被执行起来之后: 程序执行的时候会先检测
需要的动态库是否可以被加载,加载不到就会提示上边的错误信息 当动态库中的函数在程序中被调用了, 这个时候动态库才加载到内存,如果不被调用就不加载 动态库的检测和内存加载操作都是由动态连接器来完成的
动态链接器是一个独立于应用程序的进程, 属于操作系统, 当用户的程序需要加载动态库的时候动态连接器就开始工作了动态连接器根本就不知道用户通过 gcc 编译程序的时候通过参数 -L
指定的路径。 动态链接器在搜索动态库时内部有一个默认的搜索顺序,按照优先级从高到低的顺序分别是:
- 可执行文件内部的 DT_RPATH 段
- 系统的环境变量
LD_LIBRARY_PATH
- 系统动态库的缓存文件
/etc/ld.so.cache
- 存储动态库/静态库的系统目录
/lib/
,/usr/lib
等 按照以上四个顺序, 依次搜索, 找到之后结束遍历, 最终还是没找到, 动态连接器就会提示动态库找不到的错误信息。
可执行程序生成之后, 根据动态链接器的搜索路径, 我们可以提供三种解决方案,我们只需要将动态库的路径放到对应的环境变量或者系统配置文件中,同样也可以将动态库拷贝到系统库目录(或者是将动态库的软链接文件放到这些系统库目录中)。
方案1: 将库路径添加到环境变量 LD_LIBRARY_PATH 中
- 找到相关的配置文件
用户级别: ~/.bashrc
—> 设置对当前用户有效系统级别: /etc/profile
—> 设置对所有用户有效
- 使用 vim 打开配置文件, 在文件最后添加这样一句话
# 把路径写进去
export LD_LIBRARY_PATH =$LD_LIBRARY_PATH :动态库的绝对路径
- 让修改的配置文件生效
- 修改了用户级别的配置文件, 关闭当前终端, 打开一个新的终端配置就生效了
- 修改了系统级别的配置文件, 注销或关闭系统, 再开机配置就生效了
- 不想执行上边的操作, 可以执行一个命令让配置重新被加载
# 修改的是哪一个就执行对应的那个命令
# source 可以简写为一个 . , 作用是让文件内容被重新加载
source ~/.bashrc (. ~/.bashrc)
source /etc/profile (. /etc/profile)
方案2: 更新 /etc/ld.so.cache 文件
- 找到动态库所在的绝对路径(不包括库的名字)比如:
/home/robin/Library/
- 使用vim 修改
/etc/ld.so.conf
这个文件, 将上边的路径添加到文件中(独自占一行)
# 1. 打开文件
sudo vim /etc/ld.so.conf
# 2. 添加动态库路径, 并保存退出
- 更新
/etc/ld.so.conf
中的数据到/etc/ld.so.cache
中
# 必须使用管理员权限执行这个命令
sudo ldconfig
方案3: 拷贝动态库文件到系统库目录 /lib/
或者 /usr/lib
中 (或者将库的软链接文件放进去)
# 库拷贝
sudo cp /xxx/xxx/libxxx.so /usr/lib
# 创建软连接
sudo ln -s /xxx/xxx/libxxx.so /usr/lib/libxxx.s
查看能否加载到动态库
使用ldd命令可以在运行前查看可执行文件要加载的动态库以及这些动态库是否可加载
ldd 可执行文件名
列出要用到的动态库如果能加载后面会显示动态库的地址,如果不能加载后面显示not found
动态库和静态库优缺点对比
静态库
- 优点:
- 静态库被打包到应用程序中加载速度快
- 发布程序无需提供静态库,移植方便
- 缺点:
- 相同的库文件数据可能在内存中被加载多份, 消耗系统资源,浪费内存
- 库文件更新需要重新编译项目文件, 生成新的可执行程序, 浪费时间。
动态库
- 优点:
- 可实现不同进程间的资源共享
- 动态库升级简单, 只需要替换库文件, 无需重新编译应用程序
- 程序猿可以控制何时加载动态库, 不调用库函数动态库不会被加载
- 缺点:
- 加载速度比静态库慢, 以现在计算机的性能可以忽略
- 发布程序需要提供依赖的动态库
GDB
要使用gdb调试程序需要编译程序的时候添加-g参数
gcc -g test.c -o test
使用gdb打开要调试的文件
gdb 要调试的可执行程序名
在没有输入命令的情况下回车是执行上一次执行过的命令
命令 | 作用 |
---|---|
start\s | 启动调试程序,只运行一行 |
run\r | 启动调试程序,一直运行直到遇到断点停止 |
continue\c | 在start后或断点处继续运行 |
quite\q | 退出gdb |
list\l [文件名:][参数] | 默认查看当前文件的代码,默认显示10行, 加了文件名查看对应文件的代码文件名后跟:和其他参数查看对应文件对应参数的代码。执行完后当前文件就切换到指定文件了,若有参数就跳转到对应参数的代码上 参数可以加行号查看对应行的上下文代码 可以加函数名查看对应函数的上下文代码 |
break\b [文件名:] 参数 | 在当前文件设置断点,程序在执行到断点就停止,参数可以是行号或函数名,设置参数后在对应的地方打上断点 指定文件名就是在目标文件的参数位置打上断点 |
b 行数 if 变量名==某个值 | 条件断点必须满足某个条件程序才会在这个断点的位置上停止 |
info\i b | 查看断点信息。 Num:断点的编号,删除断点或者设置断点状态时使用 Enb:当前断点的状态,y表示可用,n表示不可用 What:描述断点被设置在了哪个文件的哪一行或者哪个函数上 |
delete\del\d [参数] | 删除断点 参数是断点的编号就删除对应编号的断点,可添加多个编号用空格隔开 参数是范围(n-m)删除第n到m个断点 |
disable\dis [参数] | 使对应的断点不可用,也是可以根据断点编号或范围设置 |
enable\ena [参数] | 是对应断点生效,也是可以根据断点编号或范围设置 |
print\p[/格式化字符] 变量名 | 根据格式化字符打印变量对应形式的值。若是整数默认是十进制输出 |
ptype 变量名 | 打印变量的类型 |
display[/格式化字符] 变量名 | 跟踪变量的值,实时打印出,也是可以格式化打印 |
info\i display | 显示display跟踪的变量的信息 Num:变量编号 Enb:当前变量状态(是否生效) Expression为变量或表达式的名字 |
undisplay [参数] | 取消变量跟踪,参数可以是变量编号也可以是编号范围 |
disable display [参数] | 取消变量跟踪,也是根据变量编号或编号范围 |
delete dispaly [参数] | 取消并删除变量跟踪,也是可以根据变量编号或编号范围 |
step\s | 步入,执行下一行,当遇到函数时会进入到函数内 |
next\n | 步入,执行下一行,当遇到函数时不进入到函数中 |
finish | 使用s进入到函数内时可以使用finsh跳出函数,如果函数体内有断点是跳不出来的,需要删除断点或使断点无效 |
until | 跳出循环体,若循环体中有生效断点则不可跳出,只有在一轮循环开始或结束行才可执行 |
格式化字符(/fmt) | 说明 |
---|---|
/x | 以十六进制的形式打印出整数。 |
/d | 以有符号、十进制的形式打印出整数。 |
/u | 以无符号、十进制的形式打印出整数。 |
/o | 以八进制的形式打印出整数。 |
/t | 以二进制的形式打印出整数。 |
/f | 以浮点数的形式打印变量或表达式的值。 |
/c | 以字符形式打印变量或表达式的值。 |
set设置命令
set+其他命令配合使用 | 作用 |
---|---|
args 参数 | 给程序输入命令行参数,参数之间用空格隔开,输入成功是不会给出任何信息的 |
list\l 参数 | 根据参数设置list命令显示的行数默认为10 |
var 变量名=值 | 在当前调试中更改变量的值 |
show显示命令
show+其他命令配合使用 | 作用 |
---|---|
args | 查看输入的命令行参数 |
list\l | 查看list命令信息 |
贡献者
版权所有
版权归属:PinkDopeyBug