首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++:包含vs LoadLibrary()

C++:包含vs LoadLibrary()
EN

Stack Overflow用户
提问于 2013-11-19 16:17:41
回答 5查看 3.4K关注 0票数 6

我很难理解为什么#include和LoadLibrary()都需要在C++中使用。在C++中," #include“强制预处理器用包含的文件的内容替换#include行(通常是包含声明的头文件)。据我所知,这使我能够在头部所属的外部库中使用我可能需要的例程。

那么为什么我需要LoadLibrary()呢?我就不能把图书馆本身也包括进去吗?

顺便提一句:在我比较熟悉的C#中,如果我想在程序中使用DLL中的类型或例程,我只需添加一个对DLL的引用。我不必包含任何内容,因为.NET框架显然会自动搜索所有引用的程序集,以查找我想要使用的例程(如名称空间所指定的)。

先谢谢你。

编辑:使用“定义”一词,但意思是“声明”。现在修好了。

编辑2:很难选择一个答案,许多好的回答。感谢所有的贡献。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2013-11-19 16:33:44

C++使用完全独立的编译模型;甚至可以针对尚未编写的代码进行编译。(这通常发生在大型项目中。)当您包含一个文件时,您所做的就是告诉编译器函数等存在。您不提供实现(内联函数和模板除外)。为了执行代码,您必须通过将其链接到应用程序来提供实现。这可以通过几种不同的方式发生:

  • 您有源文件;与源代码一起编译源文件,并链接到结果对象中。
  • 您有一个静态库;必须链接到它。
  • 你有一个动态库。在这里,您必须做的事情将取决于实现:在.lib下,您必须链接到一个.dll存根,并将.dll放在运行时执行时会找到它的某个地方。(将其放在与应用程序相同的目录中通常是一个很好的解决方案。)

我不太明白你需要打电话给LoadLibrary。我唯一需要这样做的时候是,我有意避免在库中直接使用任何东西,并且希望有条件地加载它,使用GetProcAddr获取我需要的函数的地址。

编辑:

因为我被要求澄清“链接”:程序翻译(从源代码到可执行文件)需要很多步骤。在传统术语中,每个翻译单元都被“编译”成一个对象文件,其中包含机器指令的图像,但是外部引用没有填充空间。例如,如果您有:

代码语言:javascript
复制
extern void function();

在源代码中(可能通过包含一个标头),然后调用function,编译器将把调用指令的地址字段保留为空,因为它不知道函数的位置。链接是获取所有对象文件并填充这些空白的过程。其中一个对象文件将定义function,链接器将在内存映像中建立实际地址,并使用该图像中的function地址填充引用function的空白。结果是可执行文件的完整内存映像。在早期的系统中,我致力于:字面意思。操作系统只需将可执行文件直接复制到内存中,然后跳入其中。像虚拟内存和共享、编写受保护的代码段这样的事情使得今天的情况更加复杂,但是对于静态链接的库或对象文件(我前面的前两种情况),差别并不大。

现代系统技术在某种程度上模糊了界限。例如,大多数Java (我认为是C#)编译器不会用机器代码生成经典的对象文件,而是生成字节代码,上面的编译和链接阶段直到运行时才会发生。一些C++编译器也只生成字节码,当代码被“链接”时就会编译字节码。这样做是为了允许跨模块优化。所有现代系统都支持动态链接:一些空白地址直到执行时才保留为空白。动态链接可以是隐式的,也可以是显式的:当它是隐式的时,链接阶段将向可执行文件中插入有关它需要的库以及找到它们的位置的信息,而OS将隐式地链接它们,或者在加载可执行文件时,或者在需要时,由试图使用一个未填充的地址插槽的代码触发。当它是显式的,您通常没有任何显式引用到您的代码中的名称。例如,在function的例子中,您不会有任何直接调用function的代码。但是,您的代码将使用LoadLibrary (或Unix下的dlopen )加载动态库,然后使用GetProcAddr (或dlsys)请求名称的地址,并通过收到的指针间接调用函数。

票数 7
EN

Stack Overflow用户

发布于 2013-11-19 16:34:50

与所有预处理功能一样,#include指令只是一个文本替换。将文本"#include“替换为该文件的内容。

通常(但不一定),这是用于包括一个头文件,该文件声明要使用的函数,即告诉编译器(在预处理器之后运行)您打算使用的某些函数是如何命名的,它们采用哪些参数,以及返回类型是什么。它没有定义函数实际上在做什么。

然后,您还需要实现这些函数。通常,如果没有在程序中实现它们,则将此任务留给链接阶段。您给出了程序依赖于链接器的库列表,并且链接器通过某种实现定义的方式(例如“导入库”)对“使其工作”所需的操作进行了区分。链接器将生成一些胶水代码,并将一些信息写入可执行文件,从而使加载程序自动加载所需的库。一切“只是有效”,而不必做一些特别的事情。

然而,在某些情况下,您希望推迟链接器阶段,并“完全动态地”手动加载,而不是自动加载。这时您必须调用LoadLibrary()GetProcAddress。前者将DLL带入内存,并进行一些设置(例如重定位),后者为您提供要调用的函数的地址。代码中的#include仍然是必需的,因此编译器知道如何处理该指针。否则,您当然可以通过其地址调用获得的函数,但不可能以有意义的方式调用该函数。

需要手动加载库(使用LoadLibrary)的原因之一是它更安全。如果您将程序链接到库中,而找不到库(或找不到符号),则应用程序将不会启动,用户将或多或少看到一条模糊的错误消息。

如果LoadLibrary失败或GetProcAddress不工作,原则上您的程序仍然可以运行,尽管功能减少了。

使用LoadLibrary的另一个例子可能是从不同的库加载一个函数的替代版本(一些程序以这种方式实现“插件”)。在编译器看来,函数“看起来”与在包含文件中定义的相同,但其行为可能与加载的二进制文件中的任何函数不同。

票数 2
EN

Stack Overflow用户

发布于 2013-11-19 16:27:17

#include只引入源代码:编译器的符号声明。库(或DLL)是对象代码:使用LoadLibrary或链接到库文件来引入对象代码。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20076600

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档