linux gcc
一、常用编译选项[*] 基本编译
gcc .c -o
示例:
gcc hello.c -o hello# 将 hello.c 编译为可执行文件 hello
./hello # 运行程序
[*] 分步编译
[*]预处置惩罚:-E(生成 .i 文件) gcc -E hello.c -o hello.i
[*]编译为汇编:-S(生成 .s 文件) gcc -S hello.i -o hello.s
[*]汇编为目标文件:-c(生成 .o 文件) gcc -c hello.s -o hello.o
[*]链接为可执行文件: gcc hello.o -o hello
[*] 调试信息
-g 选项生成带调试信息的文件(供 GDB 使用):
gcc -g test.c -o test_debug
[*] 优化选项
-O1, -O2, -O3 表示优化级别(-O2 最常用):
gcc -O2 main.c -o optimized_program
[*] 警告选项
[*]-Wall 启用所有常见警告
[*]-Werror 将警告视为错误
gcc -Wall -Werror strict_code.c -o strict_program
[*] 链接库文件
[*]-l 指定库名(如数学库 -lm)
[*]-L 指定库文件路径
gcc calc.c -lm -L/usr/local/lib -o calc
二、示例场景
1. 编译多文件项目
# 编译两个文件并链接
gcc main.c utils.c -o app 2. 生成静态库
# 1. 编译为 .o 文件
gcc -c mylib.c -o mylib.o
# 2. 打包为静态库
ar rcs libmylib.a mylib.o
# 3. 使用静态库
gcc main.c -L. -lmylib -o static_app 3. 生成动态库(共享库)
# 1. 编译为位置无关代码(-fPIC)
gcc -fPIC -c mylib.c -o mylib.o
# 2. 生成动态库(-shared)
gcc -shared mylib.o -o libmylib.so
# 3. 使用动态库(需设置库路径)
gcc main.c -L. -lmylib -o dynamic_app
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH# 临时添加库路径 三、C语言是如何编译的
1. 预处置惩罚
类比:准备食材
想象你在做一道复杂的菜,首先你需要把所有的食材准备好,比如切菜、洗菜、调味等。预处置惩罚阶段就像是这个准备食材的过程。
详细过程
预处置惩罚器(cpp)会处置惩罚源代码文件中的预处置惩罚指令,比如 #include、#define 和 #ifdef 等。它会将这些指令替换成实际的代码或数据。
[*]#include:将指定的头文件内容插入到当前文件中。
[*]#define:定义宏,替换代码中的宏定义。
[*]#ifdef / #ifndef:条件编译,根据条件决定是否包罗某段代码。
示例
#include <stdio.h>
#define PI 3.14159
int main() {
printf("The value of PI is %f", PI);
return 0;
} 预处置惩罚后,#include <stdio.h> 会被替换成 stdio.h 文件的内容,PI 会被替换成 3.14159。
2. 编译
类比:编写食谱
现在你已经准备好了食材,接下来你需要编写一个详细的食谱,告诉厨师每一步该怎么做。编译阶段就像是这个编写食谱的过程。
详细过程
编译器(gcc)会将预处置惩罚后的源代码转换成汇编代码。这个过程会检查语法错误、范例检查等。
[*]语法检查:确保代码符合C语言的语法规则。
[*]范例检查:确保变量和函数的范例使用正确。
示例
预处置惩罚后的代码会被编译成雷同以下的汇编代码(详细内容取决于编译器平静台):
main:
push rbp
mov rbp, rsp
sub rsp, 16
mov edi, OFFSET FLAT:.LC0
call puts@PLT
mov eax, 0
leave
ret 3. 汇编
类比:将食谱翻译成操纵步骤
现在你已经有了详细的食谱,接下来需要将这些食谱翻译成详细的操纵步骤,让厨师可以一步步执行。汇编阶段就像是这个翻译过程。
详细过程
汇编器(as)会将汇编代码转换成机器码,生成目标文件(.o 文件)。这个过程不会举行任何优化,只是简单地将汇编指令转换成机器码。
示例
上面的汇编代码会被转换成机器码,生成一个目标文件 main.o。
4. 链接
类比:将多个菜谱合并成一本完备的烹饪书
现在你已经有了每道菜的详细操纵步骤,接下来需要将这些步骤合并成一本完备的烹饪书,确保所有的步骤和食材都能协调工作。链接阶段就像是这个合并过程。
详细过程
链接器(ld)会将多个目标文件和库文件合并成一个可执行文件。这个过程会分析所有的符号引用,确保每个函数和变量的调用都能找到正确的定义。
[*]符号分析:确保每个函数和变量的调用都能找到正确的定义。
[*]重定位:将符号的地点修正为最终的内存地点。
假设你有两个源文件 main.c 和 utils.c,编译后会生成 main.o 和 utils.o。链接器会将这两个目标文件合并成一个可执行文件 program。
总结
[*]预处置惩罚:准备食材,处置惩罚 #include、#define 等指令。
[*]编译:编写食谱,将预处置惩罚后的代码转换成汇编代码。
[*]汇编:将食谱翻译成操纵步骤,生成目标文件。
[*]链接:合并多个菜谱,生成最终的可执行文件。
5.函数库
静态库
定义:静态库是将一系列的目标文件(.o 文件)打包成一个单独的文件,在编译链接时,链接器会把静态库中被步伐使用的代码直接复制到最终的可执行文件中。静态库的文件扩展名通常是 .a
静态连接:在静态连接中,编译器和链接器在编译阶段就把步伐所需要使用的库代码(静态库)直接合并到最终生成的可执行文件中。
[*]创建静态库:首先要有多个目标文件。假设我们有两个源文件 add.c 和 sub.c,分别实现加法和减法功能。
// add.c
int add(int a, int b) {
return a + b;
}
// sub.c
int sub(int a, int b) {
return a - b;
} 使用 `gcc` 编译这两个源文件生成目标文件:
gcc -c add.c sub.c 然后使用 `ar` 工具创建静态库:
ar rcs libmath.a add.o sub.o 这里 ar 是归档工具,r 表示将目标文件插入到归档文件中,c 表示如果归档文件不存在则创建它,s 表示写入一个目标文件索引。
如许来使用静态库
gcc main.c -L. -lmath -o main -L. 表示在当前目次下查找库文件,-lmath 表示链接名为 libmath.a 的静态库(注意,-l 背面跟的是库名去掉 lib 前缀和 .a 后缀)。
优点
[*]独立性:可执行文件包罗了静态库的所有代码,不依赖外部的库文件,移植方便。
[*]性能:由于代码已经直接嵌入到可执行文件中,运行时不需要额外的加载时间,执行效率较高。
缺点
[*]文件体积大:每个使用静态库的可执行文件都会包罗一份库代码的副本,会导致可执行文件体积变大。
[*]更新困难:如果静态库有更新,所有使用该库的可执行文件都需要重新编译链接。
动态库
定义:动态库是在步伐运行时才被加载到内存中供步伐使用的库。多个步伐可以共享同一个动态库的内存副本。是C/C++或者其他第三方软件提供的所有方法的集合,被所有步伐以链接的方式关联起来。动态库的文件扩展名通常是 .so
动态链接:在动态连接中,编译阶段只是记录步伐需要使用哪些动态库(.so 文件),然后再需要时加载,并不会把库代码复制到可执行文件中。
[*]创建动态库:同样使用之前的 add.c 和 sub.c 源文件,先编译成目标文件:
gcc -c -fPIC add.c sub.c -fPIC 表示生成位置无关代码,这是创建动态库所必需的。
使用 gcc 创建动态库:
gcc -shared -o libmath.so add.o sub.o -shared 表示生成共享库。
使用动态库:
gcc main.c -L. -lmath -o main 编译下令和静态库雷同,但在运行可执行文件时,需要确保系统能找到动态库。可以通过设置 LD_LIBRARY_PATH 环境变量来指定动态库的搜索路径:
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
./main 优点
[*]节流内存:多个步伐可以共享同一个动态库的内存副本,减少了内存的使用。
[*]更新方便:如果动态库有更新,只需要替换动态库文件,不需要重新编译使用该库的所有步伐。
缺点
[*]依赖问题:可执行文件依赖于外部的动态库文件,如果动态库文件丢失或版本不兼容,步伐可能无法正常运行。
[*]加载时间:步伐运行时需要加载动态库,会有一定的加载时间开销。
注意:linux系统中认为后缀无意义,但不是代表gcc认为后缀无意义,也就是说我们后缀不对的话gcc编译会堕落的。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]