现在已在Microsoft Connect上提交,如果您认为需要修复,请投票支持。我也大幅简化了测试用例:
byte* data = (byte*) 0x76543210;
uint offset = 0x80000000;
byte* wrong = data + offset;
byte* correct = data + (uint) 0x80000000;
// "wrong" is now 0xFFFFFFFFF6543210 (!)
// "correct" is 0xF6543210
从IL代码来看,据我所知,C#编译器已经做得没问题了,而错误在于JIT编译器。
原始问题: 这里到底发生了什么?
byte* data = (byte*)Marshal.AllocHGlobal(0x100);
uint uioffset = 0xFFFF0000;
byte* uiptr1 = data + uioffset;
byte* uiptr2 = data + (uint)0xFFFF0000;
ulong uloffset = 0xFFFF0000;
byte* ulptr1 = data + uloffset;
byte* ulptr2 = data + (ulong)0xFFFF0000;
Action<string, ulong> dumpValue =
(name, value) => Console.WriteLine("{0,8}: {1:x16}", name, value);
dumpValue("data", (ulong)data);
dumpValue("uiptr1", (ulong)uiptr1);
dumpValue("uiptr2", (ulong)uiptr2);
dumpValue("ulptr1", (ulong)ulptr1);
dumpValue("ulptr2", (ulong)ulptr2);
这个测试需要以x64平台为目标的64位操作系统。
输出:
data: 000000001c00a720 (原始指针) uiptr1: 000000001bffa720 (带有高字无法进位的指针) uiptr2: 000000011bffa720 (带有正确高字进位的指针) ulptr1: 000000011bffa720 (带有正确高字进位的指针) ulptr2: 000000011bffa720 (带有正确高字进位的指针) ^ 看这里那么这是一个bug还是我搞错了什么?
uint
变量添加指针,编译器(出于某种原因)选择进行32位算术运算,导致生成一个32位值,然后将其提升回指针(截断)。在第二种情况下,32位立即值被提升为64位值,并且编译器执行64位算术运算,保留高位字。 - Jeff Mercado(ulong) ptr2
不可能溢出到第33位。 - Roman Starkov如果指针算术操作超出了指针类型的域,结果将以实现定义的方式被截断,但不会产生任何异常。
我不确定他们所说的指针类型是什么,指针就是指针。也许这里与此有关? - Jeff Mercadoulong
+uint
并不会导致溢出,除非ulong
发生溢出。 - Roman Starkov