C# 中的 64 位指针算术,检查算术溢出改变行为 [英] 64-bit pointer arithmetic in C#, Check for arithmetic overflow changes behavior
问题描述
我有一些不安全的 C# 代码,它们在 64 位机器上运行的 byte*
类型的大内存块上执行指针运算.大多数情况下它都能正常工作,但是当事情变大时,我经常会遇到某种损坏,即指针不正确.
I have some unsafe C# code that does pointer arithmetic on large blocks of memory on type byte*
, running on a 64-bit machine. It works correctly most of the time but when things get large I often get some kind of corruption where the pointer gets incorrect.
奇怪的是,如果我打开检查算术上溢/下溢",一切正常.我没有收到任何溢出异常.但是由于性能受到很大影响,我需要在没有此选项的情况下运行代码.
The strange thing is that if I turn on "Check for arithmetic overflow/underflow" everything works correctly. I do not get any overflow exceptions. But due to the large performance hit I need to run the code without this option.
是什么导致了这种行为差异?
What could be causing this difference in behavior?
推荐答案
这是一个 C# 编译器错误 (在 Connect 上提交).@Grant 显示 C# 编译器生成的 MSIL 将 uint
操作数解释为有符号的.根据 C# 规范,这是错误的,这是相关部分 (18.5.6):
It's a C# compiler bug (filed on Connect). @Grant has shown that the MSIL generated by the C# compiler interprets the uint
operand as signed. That's wrong according to the C# spec, here's the relevant section (18.5.6):
18.5.6 指针算法
在不安全的上下文中,+
和 -
运算符(第 7.8.4 节和第 7.8.5 节)可以应用于除 void*
.因此,对于每个指针类型 T*
,隐式定义了以下运算符:
In an unsafe context, the +
and -
operators (§7.8.4 and §7.8.5) can be applied to values of all pointer types except void*
. Thus, for every pointer type T*
, the following operators are implicitly defined:
T* operator +(T* x, int y);
T* operator +(T* x, uint y);
T* operator +(T* x, long y);
T* operator +(T* x, ulong y);
T* operator +(int x, T* y);
T* operator +(uint x, T* y);
T* operator +(long x, T* y);
T* operator +(ulong x, T* y);
T* operator –(T* x, int y);
T* operator –(T* x, uint y);
T* operator –(T* x, long y);
T* operator –(T* x, ulong y);
long operator –(T* x, T* y);
给定一个指针类型T*
的表达式P
和一个int
类型的表达式N
,uint
、long
或 ulong
,表达式 P + N
和 N + P
计算 T*
类型的指针值,该值是将 N * sizeof(T)
添加到 P
给定的地址.同样,表达式 P - N
计算 T*
类型的指针值,该值是从给定地址中减去 N * sizeof(T)
通过 P
.
Given an expression P
of a pointer type T*
and an expression N
of type int
, uint
, long
, or ulong
, the expressions P + N
and N + P
compute the pointer value of type T*
that results from adding N * sizeof(T)
to the address given by P
. Likewise, the expression P - N
computes the pointer value of type T*
that results from subtracting N * sizeof(T)
from the address given by P
.
给定两个表达式,P
和 Q
,指针类型为 T*
,表达式 P – Q
计算 P
和 Q
给出的地址之间的差异,然后将该差异除以 sizeof(T)
.结果的类型总是long
.实际上,P - Q
计算为 ((long)(P) - (long)(Q))/sizeof(T)
.
Given two expressions, P
and Q
, of a pointer type T*
, the expression P – Q
computes the difference between the addresses given by P
and Q
and then divides that difference by sizeof(T)
. The type of the result is always long
. In effect, P - Q
is computed as ((long)(P) - (long)(Q)) / sizeof(T)
.
如果指针算术运算溢出指针类型的域,结果将以实现定义的方式截断,但不会产生异常.
If a pointer arithmetic operation overflows the domain of the pointer type, the result is truncated in an implementation-defined fashion, but no exceptions are produced.
您可以向指针添加 uint
,不会发生隐式转换.并且操作不会溢出指针类型的域.因此不允许截断.
You're allowed to add a uint
to a pointer, no implicit conversion takes place. And the operation does not overflow the domain of the pointer type. So truncation is not allowed.
这篇关于C# 中的 64 位指针算术,检查算术溢出改变行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!