假设您的代码是一个名为 main.cpp 的文件,如下所示:
#include<headerFromSomeLibrary>
#include<headerFromSomeOtherLibrary>
int main()
{
int var = functionFromTheLibrary();
int otherVar = functionFromTheOtherLibrary();
return var + otherVar;
}
编译将分两步进行。
首先,你将把 main.cpp 编译成一个目标文件,使用如下命令:
g++ -o main.o -c main.cpp -IheaderDirectory
其中 main.o 是要生成的目标文件的名称,headerDirectory 是包含 中包含的头文件的目录的路径main.cpp.
为了能够检查您的语法是否正确,编译器需要知道在 main.cpp 中调用但未在此处定义的类和函数的外观(在此情况下,它们来自库,但如果它们是由您在另一个文件中定义的,则它们的工作方式相同)。
这就是#include 指令的用武之地:它们指向包含被调用函数声明的头文件,并允许编译器完成其工作。如果声明位于名为 headerFromSomeLibrary.h 的头文件中,则相应的指令将是:
#include<headerFromeSomeLibrary>
此时,生成的文件 (main.o) 包含 main.cpp 中定义的函数的低级版本(在这种情况下只有 main() )。它还包含许多符号,允许识别在 main.cpp 中定义并由 main.cpp 调用的函数。
第二步是链接步骤。链接命令将如下所示:
g++ -o myProgram main.o -LsomeDirectory -lsomelibrary -lsomeotherlibrary
其中 myProgram 是您要赋予可执行文件的名称,headerDirectory 是包含头文件的目录的路径,someDirectory是包含 libsomeLibrary.a 和 libsomeOtherLibrary.a(您正在使用的库的二进制文件)的目录的路径。
与main.o类似,libsomelibrary.a和libsomeotherlibrary.a包含函数的定义(即在main()),以及标识它们的符号。链接步骤的作用是使用符号将函数定义连接到函数调用。
如果库中的函数在文件 myfunctions.h 中声明并在 myfunctions.cpp 中定义,编译指令将如下所示:
g++ -o main.o -c main.cpp
g++ -o myfunctions.o -c myfunctions.cpp
g++ -o myProgram main.o myfunctions.o
基本上,-I 选项用于告诉编译器缺少的头文件在哪里,-l 选项用于告诉它缺少的二进制文件的名称,-L 选项用于告诉它在哪里可以找到这些二进制文件。这些选项不会“停留”从一次调用 g++ 到下一次调用(这没有意义)。
现在,您询问了静态链接和动态链接之间的区别。我上面解释的实际上是静态(即编译时)链接。在静态链接的情况下,编译器将在库中获取它需要的函数定义,并将它们添加到最终的可执行文件中。这很好,因为您的可执行文件不需要其他任何东西来工作,而且您的编译器将能够在获取函数后进行优化。
但是,这并不总是您想要做的。一些库被许多不同的程序使用,您可以通过在程序之间共享库来节省大量空间。这就是动态(即运行时)链接的情况。在这种情况下,程序会在需要时简单地获取库。