代码之家  ›  专栏  ›  技术社区  ›  B.Gen.Jack.O.Neill

C运行时库第2部分

  •  4
  • B.Gen.Jack.O.Neill  · 技术社区  · 15 年前

    当我对我的老问题有更多的问题时,有人建议我创建新的问题并参考旧的问题。所以,这是最初的问题: What is the C runtime library?

    好的,从您的答案中,我现在得到静态链接库是C标准函数的微软实现。现在:

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

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

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

    3 回复  |  直到 15 年前
        1
  •  2
  •   Pindatjuh    15 年前

    C运行时中的链接是围绕“main”函数的包装;它初始化运行C代码之前所需的所有内容。它不包含(m)任何“函数”,这些函数位于C标准库(动态链接)中。

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

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

    .lib 文件是没有“d”的dll文件,因为它们 可以 静态链接。还可以动态链接库文件;这会使可执行文件变小,但会使可执行文件的加载时间变慢。

    关于winapi:大多数对c库的调用都被转换成(一些)对winapi的调用;但是只有当它们必须与操作系统(I/O等)交互时。不同之处在于,在大多数平台上,C库是相等的,因此,如果您直接使用C库而不是Windows API,则可以提高可移植性。

    更新 :

    如果完全动态链接可执行文件,您询问了如何加载DLL?嗯:你不必!“加载dll”和“调用加载dll”之间的区别是,“加载dll”由操作系统在启动应用程序时完成。操作系统将在可执行文件中搜索特定的“导入表”。这是一张表,上面写着 真正地 需要,然后才能执行(即 kernel32.dll user32.dll 在Windows上)。即使在代码运行之前,操作系统也会发出“加载dll的调用”。

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

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

        2
  •  3
  •   Greg Hewgill    15 年前

    按顺序回答问题:

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

    有一个 .dll 运行库的版本(其中 并且编译器知道如何在 .exe 告诉加载程序加载运行库的文件 动态链接库 在运行时。

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

        3
  •  1
  •   JBRWilkinson    15 年前

    在VisualC++和其他一些编译器中,CRT是为您连接的,除非您明确地告诉它不要(有时对于保持代码大小是有用的)。

    在编译器选项中,您可以选择是拥有调试版本还是发布版本,以及它的“静态链接”还是“动态链接”。

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

    动态链接将 参考 从exe调用的CRT函数。当您的exe加载并且您的代码调用引用的函数时,OS动态加载程序将看到该函数实际上位于msvcrt[d][version].dll中,然后加载该dll,查找该函数的地址,然后在代码中修正引用,以直接指向dll中的函数,这样下次,它的速度就像yo你会静态地连接它。显然,这里有一个初始启动延迟(与静态链接相比),但其优势是巨大的:系统dll被微软修补,所以你可以得到问题的更新(见上文),加载的dll是 共享 在内存中,因此,如果您引用的一个dll实际上已经被另一个进程使用,那么加载它所花费的时间就比另一个进程已经做了很多工作所花费的时间要短。

    最后,是的,像printf()和scanf()这样的crt函数最终会与win32 api对话,但并不总是您认为的那样。使用Visual Studio/Windows SDK或SysInternals“ProcExp”中的“Depends”工具查看由任何特定进程拉入的dll及其使用的函数。