参考:《鸟哥的LINUX私房菜》
一、编译与链接
假设我们先在linux的控制台界面中使用 nano hello.c ,进入文件后写一个简单的程序。现在要用GCC来编译运行,我们有两种方式:
// hello.c
#include <stdio.h>int main() {printf("Hello, World!\n");return 0;
}
1.1 分布编译链接:
gcc -c hello.c # 仅预处理、编译gcc -o hello hello.o # 仅链接# -o hello 指定输出的可执行文件名为hello
# 后面的 hello.o 是要链接的目标文件
gcc -c hello.c
这个命令告诉GCC仅进行预处理、编译(将C代码转换为汇编代码,再转换为目标代码)步骤,但不进行链接,最终生成一个 目标文件(.o
文件)。-c
选项表示编译并停止,不链接。通过先
gcc -c hello.c
生成目标文件,然后再gcc -o hello hello.o
进行链接,这种方式在大型项目中更为常见,尤其是在需要对多个源文件进行单独编译,最后统一链接的场景。这样做的好处是可以分开编译阶段和链接阶段,提高编译效率,特别是在修改了项目中的一部分代码时,只需要重新编译修改过的文件,而不是整个项目。
1.2 直接编译链接:
gcc -o hello hello.c
直接使用
gcc -o hello hello.c
时,GCC完成了整个过程——预处理、编译、汇编、以及链接。它从源代码开始,直接产生一个可执行文件。这种方式适合于简单的项目或一次性编译的情况。
1.3 最后运行可执行文件 ./hello
1.4 问题:为什么我们要制作出目标文件?
比如现在我们有一个 hello.c 主程序和 thanks.c 子程序:
hello.c (主程序)#include <stdio.h>int main() {printf("Hello, World!\n");thank();return 0;
}
thanks.c (子程序)#include <stdio.h>void thank(void) {printf("Thank you\n");
}
先对 hello.c 和 thanks.c 编译,再将 hello.o 和 thanks.o 链接到可执行文件 hello
由于我们的源代码文件有时并非仅只有一个文件,所以我们无法直接进行编译。这个时候就需要先产生目标文件,然后再以链接制作成为二进制可执行文件。
另外,假设我们更新了 thanks.c 文件的内容,那只要重新编译 thanks.c 来产生新的 thanks.o ,然后再以链接制作出新的二进制可执行文件即可,而不需要重新编译其他没有修改过的源代码文件。当我们实际在企业开发的时候,往往一个工程的代码量很大,如果将源代码全部编译则要消耗大量的时间,甚至性能不好的电脑或出现卡死的现象。
二、调用外部函数库:加入链接的函数库
hello.c (主程序不变,但我们将调用thanks.c
中的新功能)
#include <stdio.h>// 如果在hello.c中直接调用了calculate_sine,则需要声明extern double calculate_sine(double angle_degrees);int main() {printf("Hello, World!\n");double angle_in_degrees = 45.0;double sine_value = calculate_sine(angle_in_degrees);printf("The sine of %.2f degrees is: %.4f\n", angle_in_degrees, sine_value);return 0;
}
thanks.c (添加计算正弦值的功能,使用math.h
库)
#include <stdio.h>
#include <math.h> // 包含数学函数库double calculate_sine(double angle_degrees) {double angle_radians = angle_degrees * M_PI / 180.0; // 将角度转换为弧度return sin(angle_radians); // 使用sin()函数计算正弦值
}
假设我们和刚刚一样的步骤:
正确步骤:
2.1 函数库的链接(库文件位于标准路径) 一般情况
gcc -c hello.c thanks.c
gcc hello.o thanks.o -lm -o hello
[-lm]
这是一个链接选项,告诉GCC在链接过程中链接数学库(Math Library)。这里的-l
是一个通用的选项,用于指定要链接的库名,而紧跟其后的m
指定了具体的库名。这里的m
代表数学库(libm
的缩写),它是标准C库的一部分,提供了许多数学相关的函数,比如sin()
,cos()
,sqrt()
等。当编译器遇到代码中调用了这些函数时,就需要通过-lm
来确保对应的库被链接进来,以便在运行时能够找到这些函数的实现。当你在GCC命令行中使用
-lm
标志时,实际上是在告诉编译器的链接阶段需要链接标准数学库。这个库可能以动态库(如libm.so
)或静态库(如libm.a
)的形式存在。动态库在程序运行时被加载,而静态库则在编译时被直接嵌入到可执行文件中。在大多数Linux系统中,如果不特别指定,GCC通常会优先寻找动态库。
2.2 函数库的链接(库文件位于非标准路径)
如果您的库文件位于非标准路径,比如一个特定的
lib
或lib64
目录下,您需要使用-L
选项来指定额外的库搜索路径,紧接着使用-l
指定库名。例如,如果有一个库文件libcustom.so
位于/path/to/my/libs/lib64
目录下,您可以这样编译:
gcc hello.o thanks.o -L/path/to/my/libs/lib64 -lcustom -lm -o hello
-L/path/to/my/libs/lib64
告诉GCC在指定的目录中查找库文件。-lcustom
指示链接器链接名为libcustom.so
或libcustom.a
的库。