【问题标题】:Casting IntPtr to int only works sometimes将 IntPtr 转换为 int 有时仅适用
【发布时间】:2017-09-22 11:54:21
【问题描述】:

考虑这段代码:

IntPtr p = (IntPtr) (long.MaxValue); // Not a valid ptr in 32 bit, 
                                     // but this is to demonstrate the exception for 64 bit
Console.WriteLine((int)(long)p);
Console.WriteLine((int)p);

当在 64 位上编译和运行时,第二个 WriteLine 会抛出 OverflowException。这记录在here

我的问题是:为什么?

当将指针转换为Int32 时,您在概念上会丢失所有指针语义,并在语义上将指针简化为整数表示。那为什么要抛出异常,而不是截断值以适合整数呢?恕我直言,那将是最明智的做法。因为如果程序员真的想不惜一切代价避免截断,为什么首先将其放入 int 中?

这种设计是故意的,还是转换运算符的不完整实现?


在阅读了第一个 cmets 之后,我觉得有必要进行一些澄清。我的问题也可以这样解读:

是否有一个实际的用例,您可以将IntPtr 放入int,然后再将其转换回IntPtr,这样之后它仍然有效?


作为对询问用例的人们的回复,让我给出我能想到的将IntPtr 放入int 的唯一用例:实现GetHashCode for a managed wrapper object around an unmanaged对象。

【问题讨论】:

  • 如何截断指针明智?如果你真的想要这个,你总是可以明确地做到这一点。您应该感谢默认情况是检测这种情况并抛出异常,无论您认为从哲学角度来看什么是最吸引人的。请注意,转换指针的最常见原因是 在某种真空中欣赏它的位 - 这是因为最终值将被转换回来,可能是在操作之后。当价值被冲走时,祝你好运。
  • 您正试图将 64 磅的猪塞进 32 磅的戳中。它当然溢出了戳。转换为 long 仅有助于在没有尖叫声的情况下将猪减掉 32 磅,如果表达式为 unchecked,则截断是无声的。默认情况下,没有足够的程序员更改的项目设置顺便说一句。自 Windows 8.1 以来,这种溢出尤其常见,他们倾向于分配 4 GB 以上的内存。
  • 请注意,如果您在编译时启用溢出检查,即使对于常规整数,使用强制转换将 64 位值截断为 32 位也会失败。截断不是应该“在所有情况下”都起作用的东西。通过显式屏蔽位,它总是可以使工作。显式是一件好事,但尤其是当您丢弃指针的位时。
  • 在 .Net 4 之前的 .Net 版本中,除了通过 int 之外,您不能进行指针运算,这是不正确的。您始终可以将 longIntPtr 一起使用,并且您'一直能够在unsafe 代码中使用实际指针。
  • @MatthewWatson:关键是,对于 32 位代码,通过转换为 int 来进行指针运算至少是合理的。 (避免unsafe 代码有不相关的原因,比如需要更高的信任度。)当然,谨慎的开发人员不会仅仅因为他们知道他们今天的代码是 32 位而使用 32 位类型,但开发人员并不谨慎.并且还痴迷于性能。尤其是那些围绕指针投射的。

标签: c# .net casting intptr


【解决方案1】:

IntPtrint 的强制转换运算符的实现如下(来自ReferenceSource

    [System.Security.SecuritySafeCritical]  // auto-generated
    [System.Runtime.Versioning.NonVersionable]
    public unsafe static explicit operator int (IntPtr  value) 
    {
        #if WIN32
            return (int)value.m_value;
        #else
            long l = (long)value.m_value;
            return checked((int)l);
        #endif
    }

如您所见,对于 Windows x64,它将显式使用 checked 来强制溢出异常。 (我很高兴它做到了!)

相反,当您先将其转换为long,然后自己再转换为int,默认情况下将执行unchecked,因此不会抛出异常。

我只能猜测他们这样做的原因,但很明显截断指针几乎总是一件坏事,所以他们决定如果发生截断,代码应该抛出异常。

【讨论】:

  • “截断指针几乎总是一件坏事”。如果程序员想要将 intptr 的值放入普通整数类型中,它在概念上不再是指针(正如我在 OP 中所说的那样),所以你不是截断指针,而是一个恰好从指针派生的整数价值。换句话说,如果截断是一件坏事,为什么首先要把它放到一个 int 中呢?
  • @ChieltenBrinke 将指针放入 32 位 int 将适用于 32 位代码,但我认为有人想要这样做的唯一地方是互操作。我同意它很狡猾,也许在 64 位代码中将 IntPtr 转换为 int 应该总是抛出异常,无论它是否适合。但是,无论出于何种原因,微软都决定不做这个限制。
  • 无论如何,这样做的副作用是,如果您想将指针视为非指针,则必须先将其转换为long,然后才能开始扔掉位。我猜微软认为这比默默地丢弃数据更安全。请记住,这些东西是几年前加入的,当时大多数人都在运行 32 位代码,所以它可以正常工作。
猜你喜欢
  • 2013-08-12
  • 2010-10-20
  • 1970-01-01
  • 2021-11-14
  • 2012-10-04
  • 2012-12-29
  • 2011-01-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多