为什么我需要将lib文件链接到我的项目?

我正在创建一个使用DLL的项目。 要构建我的项目,我需要包含一个头文件和一个lib文件。 为什么我需要包含相应的lib文件? 不应该头文件声明所有需要的信息,然后在运行时加载任何所需的库/ DLL?

谢谢

在许多其他语言中,只需要头文件即可。 但Windows上常见的C链接器一直使用导入库,C ++链接器也跟着它,而且改变可能为时已晚。

作为一个思想实验,人们可以想象这样的语法:

__declspec(dllimport, "kernel32") void __stdcall Sleep(DWORD dwMilliseconds); 

有了这些信息,编译器/链接器工具链可以完成剩下的工作。

作为另一个例子,在Delphi中,可以使用隐式链接导入此函数,如下所示:

 procedure Sleep(dwMilliseconds: DWORD); stdcall; external 'kernel32'; 

这只是表明导入库不是先验的,对于链接到DLL是必不可少的。

这是一个所谓的“导入库”,它包含最少的布线,稍后(在加载时)要求操作系统加载DLL。

不,头文件不够苛刻。 头文件可以只包含函数和类的声明以及您需要的其他内容, 而不是它们的实现。

这段代码之间存在着天壤之别:

 void Multiply(int x, int y); 

而这段代码:

 void Multiply(int x, int y) { return x * y; } 

第一个是声明,第二个是定义或实现。 通常第一个示例放在头文件中,第二个放在.CPP文件中(如果您正在创建库)。 如果你在第一个包含一个标题并且没有任何链接,你的应用程序应该如何知道如何实现Multiply?

现在,如果您使用包含全部内联代码的头文件,那么您不需要链接任何内容。 但是,即使一个方法没有内联,但是它的实现是在.CPP文件中编译成.lib文件,而不是你需要在.lib文件中链接。

[编辑]使用导入库,您告诉链接器不要将导入代码的实现细节包含在二进制文件中。 相反,操作系统会在运行时将导入DLL加载到您的进程中。 这将使您的应用程序更小,但您必须随身携带另一个DLL。 如果库的实现发生了变化,您可以将另一个DLL重新发送给您的客户,而不必重新启动整个应用程序。

还有另一个选项,您可以在库中链接,而不需要发送另一个DLL。 该选项是链接器将实现包含在您的应用程序中的位置,使其更大。 如果您必须更改导入库中的实现细节,则必须重新编译并重新链接整个应用程序,并将整个应用程序重新转移给客户。

DLL是Windows(MS / Intel)的东西。 (生成的)lib包含调用DLL所需的代码,它将“正常”函数暴露给应用程序的其余部分。

这里的建设过程分为两个相关阶段:

  • 编译:从源代码到目标文件。 在编译期间,编译器需要知道哪些外部事物可用,因为需要声明。 设计用于多个编译单元的声明在标题中分组。 所以你需要库的标题。

  • 链接:对于静态库,您需要库的编译版本。 对于动态库,在Unix中你需要库,在windows中,你需要“导入库”。

您可以认为库也可以嵌入声明,或者标题可以包含需要链接的库。 第一种通常用其他语言完成。 第二种有时可以通过C和C ++中的编译指示来获得,但是没有标准的方法来执行此操作并且与常见用法相冲突(例如,在几个中为相同的声明提供代码变体的库中选择一个库,例如debug /发布单线程/multithreading)。 这两种选择都与C和C ++的简单编译模型完全吻合,后者源于60年代。

头文件由编译器使用。 它包含将使用的函数,类和全局变量的所有前向声明。 它也可能包含一些内联函数定义。

编译器使用它们来为它提供编译代码所需的最低限度信息。 它不包含实现细节。

但是,您仍然需要链接所有函数和您告诉编译器的变量定义。 如果不这样做将导致链接器错误。 通常,这包含在其他目标文件中,这些目标文件可以连接到单个静态库中。

在DLL(或.so文件)的情况下,我们仍然需要告诉链接器DLL或共享对象中缺少的符号在哪里。 在Windows上,此信息包含在.lib文件中。 这将生成用于在运行时加载和链接代码的代码。

在unix上,dll和lib文件合并为一个.so文件,您必须链接到该文件中的链接器错误。

您仍然可以使用没有.lib文件的dll,但是您必须使用操作系统API手动加载和链接所有符号。

从1000英尺开始,lib包含dll导出的函数列表以及调用所需的地址。