【问题标题】:Marshal.SizeOf(typeof(IntPtr)) vs sizeof(IntPtr)Marshal.SizeOf(typeof(IntPtr)) 与 sizeof(IntPtr)
【发布时间】:2017-05-18 15:38:23
【问题描述】:

我知道Marshal.SizeOf()sizeof() 有多么根本的不同。但是在IntPtr 的情况下,无论 CPU 架构如何,它们都总是返回完全相同的东西吗?

【问题讨论】:

  • 你的意思是它总是4 (32x) 还是8 (64x)?你很幸运IntPtr 结构不需要more managed memory,但它可能有一天或在某个平台上,最好继续使用合适的。
  • 你能解释一下为什么你关心内存大小和线上大小是否相同吗?你只是问一个琐碎的问题,还是你的问题有什么目的?
  • @EricLippert 主要是要弄清楚,特别是考虑到IntPtrMarshal.SizeOf()sizeof() 是否可以互换。如果不是,为什么要使用一个而不是另一个。
  • 等等,你在问题中说我知道它们有多么根本不同。如果您认为它们可能可以互换并且不知道何时会使用其中一种,那么您知道它们有何不同?
  • 假设我有一个函数告诉我一个烤面包机占用了多少柜台空间,另一个函数告诉我邮寄烤面包机需要多大的盒子。问“对于这个特殊的烤面包机,这两个尺寸是否相同”有什么意义吗?也许他们是,也许他们不是,但是你为什么要关心?您会在布置厨房时使用第一种方法,而在包装邮车时使用第二种方法,而且您永远不会交换它们,因为那是一个非常糟糕的主意。

标签: c# .net


【解决方案1】:

一方面,有原始类型:

| <b>x64</b> | <b>x86</b>
| Marshal. | Marshal.
Primitive | SizeOf&lt;T&gt;() sizeof(T) | SizeOf&lt;T&gt;() sizeof(T)
========= | =========== =========== | =========== ===========
Boolean | 4 &lt;-&gt; 1 | 4 &lt;-&gt; 1
Byte | 1 1 | 1 1
SByte | 1 1 | 1 1
Int16 | 2 2 | 2 2
UInt16 | 2 2 | 2 2
Int32 | 4 4 | 4 4
UInt32 | 4 4 | 4 4
Int64 | 8 8 | 8 8
UInt64 | 8 8 | 8 8
IntPtr | 8 8 &lt;-&gt; 4 4
UIntPtr | 8 8 &lt;-&gt; 4 4
Char | 1 &lt;-&gt; 2 | 1 &lt;-&gt; 2
@ 987654343@
Single | 4 4 | 4 4

除此之外,对于 .NET 中的 struct (ValueType) 实例,内部托管布局和封送处理图像之间可能存在显着差异,无论是在总大小还是字段布局顺序上。即使对于所谓的formatted classes,后者也是如此。[1]

实际上很少需要有关实际托管结构布局的信息,实际上 .NET 竭尽全力试图使其无法被发现。您也不能影响结构的内部布局,这正是Marshal 层提供了专门声明互操作所需的任何布局的能力的原因。

这是一个需要知道结构的运行时内存映像的真实大小的用例:假设您将结构的托管数组用于某种存储 blob 概念,并且您希望每个块(即数组)保持不变在固定的总分配大小下,比如 ~84,800 字节——显然在这种情况下要远离LOH。您希望这个存储类是一个通用类,使用任意的ValueType 类型进行参数化,该类型定义了“记录”或表条目。要确定可以进入每个托管数组块的结构的数量,您需要发现在运行时给出的结构的真实大小,因此您可以将 84,800 除以该值。

更详细地检查 marshalingmanaged-internal 结构布局之间可能出现的差异、填充和大小,请参阅我对“How do I check the number of bytes consumed by a structure?”的扩展回答



[1.] "格式化类 是一种引用类型,其布局由 StructLayoutAttribute 属性指定,如 LayoutKind.ExplicitLayoutKind.Sequential。"
@987654326 @

【讨论】:

    【解决方案2】:

    首先,你为什么想知道?如果你的代码写得很好,你应该不需要对sizeofMarshal.SizeOf 返回什么做任何假设。您会使用编组器来编组您的任何IntPtr 实例吗?然后使用Marshal.SizeOf。您的变量是永远不会离开托管世界还是使用自定义编组?然后使用sizeof(或IntPtr.Size,因为它不需要unsafe 块)。在这两种情况下,您都不会关心它们是否返回相同的值。这是实际的答案。

    关于理论。根据C# language specificationsizeof(IntPtr) 返回的值是“该类型变量中的总字节数,包括任何填充”(因为IntPtr 是一个结构)。但是,它还指出该值是“实现定义的”。因此,如果您想获得技术知识,C# 规范只会说“弄清楚”。

    也就是说,IntPtr 的文档清楚地表明该类型在 32 位平台上是 32 位的,在 64 位平台上是 64 位的,ECMA-335 文档表明 IntPtr 是一个特殊的内置-in 类型对应于native int,所以我认为我们可以暂时得出结论,sizeof(IntPtr) 在任何声称遵循规范的实现上都是可预测的:4 在 32 位平台上,8 在 64 位平台上. IntPtr.Size 是另一种选择,它明确记录了这一点。

    Marshal.SizeOf(typeof(IntPtr)) 是另一种野兽。除了“非托管类型的大小”之外,它没有准确记录它将返回的内容;在水下,它会调用 CLR 中的一些本机代码,向 TypeHelper 询问大小的底层类型。对于 IntPtr,这将返回 sizeof(void*)(在 C++ 中),对于绝大多数 C++ 编译器和平台,当然,在 32 位平台上为 4,在 64 位平台上为 8。

    从技术上讲,sizeof(IntPtr)Marshal.SizeOf(typeof(IntPtr)) 可能不同。但这不是您通常会关心的事情,因为运行时和抖动(或 AOT 编译器)的组合不使 sizeof(IntPtr) 等于 Marshal.SizeOf(typeof(IntPtr)) 是不明智的——否则运行时是只是让自己的生活更加艰难。另一方面,正如我所指出的,您通常也没有任何理由需要依靠它们是相同的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-05
      • 2015-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多