在处理大型数组时,我正在进行不安全的指针计算,如下所示:
*c++ = *a++ - *b++;
它按预期工作。但对于就地操作,我也需要右侧的c指针:
[STAThread]
unsafe static void Main(string[] args) {
double[] arr = new double[] { 2, 4, 6, 8, 10 };
double scalar = 1;
fixed (double* arrP = arr) {
double* end = arrP + arr.Length;
double* p = arrP;
double* p2 = arrP;
while (p < end) {
// gives: 3,5,7,9,2,4827634676971E+209
*p++ = *p - scalar;
// gives correct result: 1,3,5,7,9
//*p = *p - scalar;
//p++;
}
}
Console.WriteLine(String.Join<double>(",", arr));
Console.ReadKey();
}
指针在取消引用之前递增。根据优先规则(++之前的++),这是正确的。但现在新值将写入递增地址,而不是原始地址。为什么会这样?
我发现了这个问题:dereference and advance pointer in one statement?。但它只处理右侧的* c ++表达式。为什么写访问与读访问不同?
此外,我们非常感谢C#规范中指针类型的优先规则的链接。到目前为止找不到它们。
@EDIT:请注意,我们在这里讨论的是C#,而不是C或C ++。即使我希望这里的差异不是太大。另外,就像上面的例子一样,我知道,可以通过在下一个代码行中递增指针来防止这个问题。我想知道,为什么行为如上所述。
答案 0 :(得分:15)
关键是这句话就在这里:
指针在取消引用之前递增。 根据优先规则(++之前的*),这是正确的。但现在新值被写入增量地址,而不是原始地址。为什么会这样?
这意味着您认为副作用的优先顺序和顺序是相关的。 他们不是。副作用发生在从左到右,句号,故事结束。如果你有
A().x = B() + C() * D();
然后乘法发生在加法之前,因为乘法优先级更高。添加发生在赋值之前,因为添加优先级更高。 A(),B(),C()和D()的副作用以从左到右的顺序发生,而与操作符的优先级无关。执行顺序无关< / em>优先。 (如果由于处理器缓存问题而从另一个线程观察,可能会观察到副作用以不同的顺序发生,但是一个线程中的副作用总是以从左到右的顺序观察。)
在您的示例中,p++
位于右侧*p
的左侧,因此p++
的副作用发生之前观察右侧的副作用。更具体地说,赋值运算符对变量的操作是:
第一步 - 评估左边变量的地址 - ++是什么。
这在C#规范中明确定义;有关详细信息,请参阅有关运算符优先级和执行顺序的部分。
如果这个主题让你感兴趣,请参阅我关于优先顺序,关联性和顺序之间差异细节的大量文章:
http://blogs.msdn.com/b/ericlippert/archive/tags/precedence/
如果你不理解++的工作原理 - 不幸的是,几乎没有人这样做 - 看到这个问题:
What is the difference between i++ and ++i?
如果你不理解作业是如何运作的 - 我很惊讶地发现几乎没有人理解作业的作用 - 请参阅:
http://blogs.msdn.com/b/ericlippert/archive/tags/simple+assignment/
http://blogs.msdn.com/b/ericlippert/archive/tags/compound+assignment/
其他答案指出,在C和C ++编程语言中,如果副作用及其观察在同一“序列点”内,语言规范没有指定副作用的顺序,因为它们在这里。在C中,允许++的副作用在语句结束之前的任何时间发生。在赋值之后,在赋值之前,每当由编译器自行决定。 C#不允许这种格局。在C#中,观察到左边的副作用是在右边的代码执行时发生的。
此外,我们非常感谢C#规范中指针类型的优先规则的链接。到目前为止找不到它们。
您想要的规格部分是18.5,其中说明:
语法隐含了不安全运算符的优先级和相关性。
所以阅读语法并解决它。首先阅读附录B第3节中的语法。