【问题标题】:Understanding of Managed C++对托管 C++ 的理解
【发布时间】:2017-07-16 21:23:27
【问题描述】:

我无法理解托管 C++ 如何工作和编译。

在 .NET Framework 中,您可以使用 C#/VB/F#/..etc 开发代码。并且所有这些语言都将被编译为与 Java 字节码类似的相同通用中间语言 (CIL)。从理论上讲,CIL 可以安装在任何平台上(Mono 使其实用)。在 Windows 上,CLR 将 CIL 编译为本机代码 Just-In-Time (JIT),一切都运行顺利,一切顺利。

现在,托管 C++ 是如何编译的?它是否编译为 CIL 代码并等待 CLR 使用 JIT 运行它?我认为不会,因为托管 C++ 可以使用标准 C++ 代码(未编译为 CIL)。此外,它如何能够使用 .NET 程序集(它们是 CIL)?

如果有任何帮助,我将不胜感激。 谢谢

编辑:

我看过这个answer。它指出,在 C++/CLI 中,托管代码被编译为 MSIL,您可以选择将非托管代码编译为本机代码或 MSIL。因此,我现在了解如何调用 .NET 程序集。

无论如何,如果将非托管代码编译为本机代码,我仍然不明白 C++ 非托管代码如何与托管代码在同一个程序集中运行。有什么想法吗?

【问题讨论】:

    标签: c++ .net clr managed cil


    【解决方案1】:

    这是一个很大的话题,具有非常坚韧的实现细节。很难解决所有问题,但问题中存在一些误解。让我们解决这些问题,可能有助于进入下一阶段。

    此外,它如何能够使用 .NET 程序集(即 CIL)?

    不仅仅是 CIL,链接器还会生成一个混合模式 程序集。包含 .NET 元数据 + msil 本机代码。事实上,就OS loader而言,正常的是可执行文件中的native code。与本机 C++ 编译器生成的类型没有什么不同。它像纯本机可执行映像一样被加载和重定位。奇怪的是 .NET 元数据 + msil。对于加载器来说,它看起来就像是一块数据,根本不会触及它。只有 CLR 可以。

    ...使用标准 C++ 代码(未编译为 CIL)

    不太准确,原生C++代码可以编译成msil机器码。您得到什么取决于是否使用了 /clr 编译选项或在函数级别有效的 #pragma managed。 CIL 不能很好地与 Java JVM 中使用的字节码进行比较。它更强大,可以支持任何符合 C++03 的原生 C++ 代码。有时您这样做是为了利用反向 pinvoke(本机代码调用托管代码)。有时它是偶然完成的,并且完全有太多的本机 C++ 代码被编译为 msil。抖动产生的机器代码不是最优的(它在时间限制下优化)并且不以任何方式进行管理。它是不可验证的,也不会得到垃圾收集器的喜爱。

    CIL 的最佳心理形象是在前端(解析器)和后端(代码生成器和优化器)之间的任何本机 C++ 编译器中使用的中间表示。通常是不可见的实现细节,但当您使用使用 LLVM 的 C++ 编译器(如 Clang 那样)时会变得更加明显。 .NET 即时编译器在运行时执行 LLVM 在编译时执行的操作。


    当托管代码调用本机代码(或相反)时,大多数程序员的脑海中都会出现一个巨大的模式切换。这根本不准确。您可能想看看this post,它显示了 C++ 编译器后端生成的机器代码与抖动之间的区别。关键是它几乎相同,这是确保托管代码与本机代码竞争的基本功能。有助于阐明托管代码调用本机代码或相反的方式没有那么特别。

    另一个误解是托管代码自动更安全。不完全正确,像 C# 这样的语言让您可以像使用 C++ 一样使用指针和在堆栈上乱写乱写,并且您可以像使用 C++ 一样轻松地破坏内存。它只是更好地分区,它迫使您使用 unsafe 关键字明确说明它。在 C++/CLI 上没有这样的限制,什么都可以。

    托管代码和本机代码的本质区别在于编译 msil 时抖动生成的数据结构。您无法从本机编译器获得的额外数据。垃圾收集器需要这些数据,它告诉它如何找到对象根。更多关于 this post 中的数据的信息。必须符合该数据并允许 GC 完成其工作是使托管代码在运行时变慢的原因。

    【讨论】:

    • 很好的答案。尤其是这样:“CIL 与 Java JVM 中使用的字节码相比并没有那么好。它更强大,可以支持任何符合 C++03 的本机 C++ 代码。”当我找到一份 Java 工作时,我看着他们的字节码规范哭了!
    【解决方案2】:

    Manged C++ 已弃用。今天要编写本机 C++ 和 Manged 代码,您需要使用 C++\CLI。它符合 CLR 并且可以运行其他 .net 程序集。您还可以使用本机调用,这些对于本机代码和 .NET 代码之间的互操作性非常有用 要调用 .NET 程序集,请在您的项目中添加对该程序集的引用并添加您的代码:

    using namespace System;
    

    【讨论】:

    • 我很欣赏关于 C++/CLI 的说明。但是,您仍然可以将 C++/CLI 的非托管部分编译为本机代码(非 MSIL)。那么,由于代码的托管部分将被强制编译为 MSIL,那么它是如何工作的呢?
    • @Everyone,文件格式仍然是 PE 文件,可以像任何其他文件一样包含本机代码(以交叉移植为代价)。 calli 指令可用于调用本机方法。
    • 如果您不能不将 C++/CLI 编译为本机(仅)。如果您需要托管代码
    • @BrianReichle 是的,汉斯在他的回答中解释了这一点。 CLR 处理 MSIL 代码,而 OS 处理本机编译代码,因此程序能够在混合模式下运行。
    • @RafiZisman C++/CLI 与托管 C++ 并没有太大不同,它只是标准化的有组织的版本,因为它更易于使用。在幕后,编译和解释工作相同。
    猜你喜欢
    • 2010-12-28
    • 2016-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-15
    • 2014-10-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多