【问题标题】:C Run-Time library part 2C 运行时库第 2 部分
【发布时间】:2010-05-10 23:09:27
【问题描述】:

当我对旧问题有其他问题时,有人建议我创建新问题并参考旧问题。所以,这是原来的问题:What is the C runtime library?

好的,从您的回答中,我现在了解到静态链接库是 Microsoft 对 C 标准函数的实现。现在:

如果我做对了,方案如下:我想使用 printf(),所以我必须包含 <stdio.h>,它只是告诉编译器有一个带有这些参数的函数 printf()。现在,当我编译代码时,因为 printf() 是在 C 标准库中定义的,并且因为 Microsoft 决定将其命名为 C 运行时库,所以它会自动从 libcmt.lib 静态链接(如果在编译器中设置了 libcmt.lib)编译时间。我问,因为在维基百科上,在关于运行时库的文章中,运行时库是在运行时链接的,但是 .lib 文件是在编译时链接的,对吗?

现在,让我感到困惑的是什么。有 .dll 版本的 C 标准库。但我认为要链接 .dll 文件,您必须实际调用 winapi 程序来加载该库。那么,如果没有静态库提供代码告诉 Windows 从 dll 加载所需的函数,这些函数如何动态链接?

关于这个主题的最后一个问题 - C 标准库函数是否也调用 winapi,即使它们不是像更高级的 WinAPI 函数那样的 .dll 文件?我的意思是,最后要访问帧缓冲区并打印一些东西,你必须告诉 Windows 去做,因为操作系统不能让你直接操作硬件。我认为操作系统必须以相同的方式在类似版本中支持所有 C 标准库函数,因为它们是静态链接的,并且可以不同地支持更复杂的 WinAPI 调用,因为新版本的操作系统可以在 .dll 文件中进行调整.

【问题讨论】:

    标签: c runtime


    【解决方案1】:

    按顺序解决您的问题:

    维基百科误导了你。运行时库并不总是在运行时链接,并且在您选择静态链接运行时的情况下也不会链接 (libcmt.lib)。

    有一个.dll 版本的运行时库( 在运行时链接)并且编译器知道如何在.exe 文件中生成适当的指令来告诉加载器加载运行时库.dll 在运行时。

    这里有两个 API。一个是 Win32 API,它是 Windows 操作系统本身支持的功能列表。另一个 API 是由 C 编程语言标准定义的 C 运行时 API。一些 C 运行时库函数,例如打开和读取文件,最终会调用 Win32 API 来执行实际的文件 I/O。其他 C 库函数,例如 strlen(),不与操作系统交互,并且完全使用运行时库本身内部的代码实现。

    【讨论】:

    • 好的,谢谢。我知道一些 C 标准库函数不需要操作系统调用,例如所有 math.h 函数。
    • 对于某些函数,例如 strlen(),它们可能由编译器实现为内联汇编语言插入 - 可能不是在 Windows 上由 MSVC 实现,而是在具有其他编译器的其他平台上。
    • @Jonathan Leffler:MSVC 实际上有很多这样的内在函数:msdn.microsoft.com/en-us/library/26td21ds.aspx
    【解决方案2】:

    C 运行时中的链接是您的“主”函数的包装器;它会在您的 C 代码运行之前初始化所需的一切。它不包含(m)任何“函数”,它们位于 C 标准库(动态链接)中。

    我认为您误解了动态链接:它是由操作系统完成的。因此,您告诉操作系统您的可执行文件需要 DLL abcd。当您执行可执行文件时,操作系统会将可执行文件加载到内存位置,并在可执行文件中读取运行它所需的内容。然后它会抓取这些 DLL 并将它们粘贴到可执行文件的内存区域中,然后它会告诉您的代码 a 加载到 xb 加载到 y 等,所以您的代码可以调用它的函数。

    有时,编译器会在编译时包含(称为静态链接)库:他们这样做,这样操作系统就不必在运行时加载它,因此加载速度更快。

    .lib 文件是不带“D”的 DLL 文件,因为它们可以静态链接。也可以动态链接库文件;这会使您的可执行文件更小,但会使您的可执行文件的加载时间变慢。

    关于 WinAPI:对 C 库的大多数调用都转换为(部分)对 WinAPI 的调用;但前提是它们必须与操作系统交互(I/O 等)。不同之处在于 C 库在大多数平台上是相同的,因此如果您使用 C 库而不是直接使用 Windows API 会增加可移植性。

    更新

    您问如果完全动态链接可执行文件,如何加载 DLL?好吧:你不必! “加载dll”和“调用加载dll”之间的区别是;当您启动应用程序时,操作系统会完成“加载 dll”。操作系统将在您的可执行文件中搜索特定的“导入表”。这是一个表格,说明它在执行之前真正需要哪些 DLL(即 Windows 上的 kernel32.dlluser32.dll)。即使在您的代码运行之前,操作系统也会“调用加载 dll”。

    “调用加载 dll”也存在于 kernel32.dll 中,以便调用您的代码:可以在 您的 代码运行时加载/卸载 DLL。如果您有一个庞大的代码库,并且您希望在整个应用程序生命周期(例如,在启动期间)卸载该一次性使用库来释放内存,则可能是这种情况。如果您不再使用该功能,您可以卸载 DLL。但也有可能您需要一些尚未加载的功能来加快加载速度。如果您自己需要该函数,则可以加载该 DLL。这是非常先进的东西,而且大多数时候,操作系统无论如何都会交换掉未使用的 DLL(“删除”内存,字面意思是:它将不常用的内存(如未使用的 DLL)移动到大量的地方存储,就像硬盘一样。如果你需要它,它会自动“交换”回来!)。

    所以:您不必太担心在 Windows 上加载/卸载 DLL。如果你有一个好的链接器,并告诉它动态链接库,一切都会很好。

    【讨论】:

    • 感谢您的回答。我想我理解 dll 对。我是,当你想在其他静态链接的程序中直接加载一些 dll 时,你必须调用 WinAPI 来完成。所以我提到当你唯一的库是 dll 本身时,你如何调用操作系统来加载 dll?
    【解决方案3】:

    在 Visual C++ 和其他一些编译器中,CRT 会为您链接,除非您明确告诉它不要这样做(有时有助于减小代码大小)。

    在编译器选项中,您可以选择是否具有 Debug 或 Release 版本,以及它是静态链接还是动态链接。

    静态链接将来自您调用的所有 CRT 函数的所有实际代码直接放入您的 EXE。这对于减少您需要多少外部依赖项很有用——您可以只运行 EXE 而不必担心您是否安装了正确的 xxxx.dll。缺点是您提供的 CRT 函数可能存在问题(安全漏洞、崩溃、竞争条件),您的最终用户解决这些问题的唯一方法是您生成新的 EXE。

    动态链接为您从 EXE 调用的 CRT 函数提供了一个引用。当您的 EXE 加载并且您的代码调用引用的函数时,操作系统动态加载器将看到该函数实际上在 MSVCRT[D][version].dll 中并加载该 DLL,查找函数的地址然后修复代码中的引用,使其直接指向 DLL 中的函数,这样下一次,它就像静态链接它一样快。显然,这里有一个初始启动延迟(与静态链接相比),但优点是巨大的:系统 DLL 由 Microsoft 修补,因此您可以获得问题的更新(见上文),加载的 DLL 是共享在内存中,因此如果您引用的 DLL 实际上已被另一个进程使用,则加载它所需的时间更少,因为其他进程已经完成了很多工作。

    最后,是的,像 printf() 和 scanf() 这样的 CRT 函数最终会与 Win32 API 对话,但并不总是如您所想的那样。使用 Visual Studio/Windows SDK 或 SysInternals 的“ProcExp”中的“DEPENDS”工具来查看任何特定进程提取了哪些 DLL 以及它们正在使用哪些函数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-01-15
      • 1970-01-01
      • 1970-01-01
      • 2021-01-31
      • 1970-01-01
      • 2020-02-17
      • 2011-01-09
      相关资源
      最近更新 更多