我正在创建一个使用DLL的项目。要构建我的项目,我需要包含一个头文件和一个lib文件。为什么我需要包含相应的lib文件?不应该头文件声明所有需要的信息,然后在运行时加载任何所需的库/ DLL?
由于
答案 0 :(得分:4)
不,头文件已经不够了。头文件只能包含函数和类的声明以及您需要的其他内容,不它们的实现。
此代码之间存在天壤之别:
void Multiply(int x, int y);
和这段代码:
void Multiply(int x, int y)
{
return x * y;
}
第一个是声明,第二个是定义或实现。通常第一个示例放在头文件中,第二个放在.CPP文件中(如果要创建库)。如果你在第一个包含一个标题并且没有链接任何内容,你的应用程序应该如何知道如何实现Multiply?
现在,如果您使用包含全部内联代码的头文件,那么您不需要链接任何内容。但是,即使一个方法没有内联,但是它的实现是在.CPP文件中编译成.lib文件,而不是你需要在.lib文件中链接。
[编辑] 通过使用导入库,您告诉链接器不要将导入代码的实现细节包含在二进制文件中。相反,操作系统会在运行时将导入DLL加载到您的进程中。这将使您的应用程序更小,但您必须随身携带另一个DLL。如果库的实现发生了变化,您可以将另一个DLL重新发送给您的客户,而不必重新发送整个应用程序。
还有另一个选项,您可以在库中链接,而不需要发送另一个DLL。该选项是链接器将实现包含在您的应用程序中的位置,使其更大。如果您必须更改导入库中的实现细节,则必须重新编译并重新链接整个应用程序,并将整个应用程序重新转移给客户。
答案 1 :(得分:3)
这是一个所谓的“导入库”,它包含最少的布线,稍后(在加载时)要求操作系统加载DLL。
答案 2 :(得分:3)
在许多其他语言中,只需要头文件即可。但Windows上常见的C连接器一直使用导入库,C ++链接器也是如此,改变可能为时已晚。
作为一个思想实验,人们可以想象这样的语法:
__declspec(dllimport, "kernel32") void __stdcall Sleep(DWORD dwMilliseconds);
有了这些信息,编译器/链接器工具链可以完成剩下的工作。
作为另一个例子,在Delphi中,可以使用隐式链接导入此函数,如下所示:
procedure Sleep(dwMilliseconds: DWORD); stdcall; external 'kernel32';
这只是表明导入库不是先验的,对于链接到DLL是必不可少的。
答案 3 :(得分:2)
DLL是Windows(MS / Intel)的东西。 (生成的)lib包含调用DLL所需的代码,它将“正常”函数暴露给你的应用程序的其余部分。
答案 4 :(得分:1)
此处的构建过程分为两个相关阶段:
编译:从源代码到目标文件。在编译期间,编译器需要知道哪些外部事物可用,因为需要声明。设计用于多个编译单元的声明在标题中分组。所以你需要库的标题。
链接:对于静态库,您需要库的编译版本。对于动态库,在Unix中你需要库,在windows中,你需要“导入库”。
您可以认为库也可以嵌入声明,或者标题可以包含需要链接的库。第一种通常用其他语言完成。第二种有时可以通过C和C ++中的编译指示来获得,但是没有标准的方法来执行此操作并且与常见用法相冲突(例如,在几个中为相同的声明提供代码变体的库中选择一个库,例如debug /发布单线程/多线程)。这两种选择都与C和C ++的简单编译模型完全吻合,后者源于60年代。
答案 5 :(得分:1)
头文件由编译器使用。它包含将使用的函数,类和全局变量的所有前向声明。它也可能包含一些内联函数定义。
编译器使用它们来为它提供编译代码所需的最低限度信息。它不包含实现细节。
但是,您仍然需要链接所有函数和您告诉编译器的变量定义。如果不这样做将导致链接器错误。通常这包含在其他目标文件中,这些文件可以连接到一个静态库中。
对于DLL(或.so文件),我们仍然需要告诉链接器DLL或共享对象中缺少的符号在哪里。在Windows上,此信息包含在.lib文件中。这将生成用于在运行时加载和链接代码的代码。
在unix上,dll和lib文件被合并到一个.so文件中,你必须链接到这个文件来链接错误。
您仍然可以使用没有.lib文件的dll,但是您必须使用操作系统API手动加载和链接所有符号。
答案 6 :(得分:1)
从1000英尺开始,lib包含dll导出的函数列表以及调用所需的地址。