C# 为什么*p++;=*p-a给出奇怪的结果?

C# 为什么*p++;=*p-a给出奇怪的结果?,c#,pointers,operator-precedence,C#,Pointers,Operator Precedence,在使用大型数组时,我会执行不安全的指针计算,如下所示: *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) { dou

在使用大型数组时,我会执行不安全的指针计算,如下所示:

*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(); 
}
[STAThread]
不安全的静态void Main(字符串[]args){
double[]arr=新的double[]{2,4,6,8,10};
双标量=1;
固定(双*arrP=arr){
双*端=arrP+arr.长度;
双*p=arrP;
双*p2=arrP;
while(p
指针在取消引用之前递增。根据优先级规则(++before*),这是正确的。但现在新值被写入递增的地址,而不是原始地址。为什么会这样

我发现这样的问题:。但它只处理右侧的*c++表达式。为什么写访问与读访问不同

此外,如果能链接到C#规范中指针类型的前置规则,我们将不胜感激。到目前为止找不到他们


编辑:请注意,这里我们讨论的是C,而不是C或C++。即使我预计这里的差异不会太大。另外,就像上面的例子一样,我知道,可以通过在下一个代码行中增加指针来防止这个问题。我想知道,为什么这种行为会如此

关键是这句话:

指针在取消引用之前递增根据优先规则(++before*),这是正确的。但现在新值被写入递增的地址,而不是原始地址。为什么会这样

这意味着你相信副作用的先后顺序是相关的它们不是。副作用的发生顺序从左到右,周期,故事的结尾。如果你有

A().x = B() + C() * D();
然后乘法发生在加法之前,因为乘法的优先级更高。加法发生在赋值之前,因为加法的优先级更高A()、B()、C()和D()的副作用按从左到右的顺序发生,与运算符的优先级无关。执行顺序与优先级无关。(如果由于处理器缓存问题而从另一个线程进行观察,可能会发现副作用的发生顺序不同,但一个线程中的副作用总是按从左到右的顺序进行观察。)

在您的示例中,
p++
位于右侧
*p
左侧,因此
p++
的副作用发生在观察右侧副作用之前。更具体地说,赋值运算符对变量的操作是:

  • 计算左侧变量的地址
  • 计算右侧,必要时将其转换为变量类型
  • 将值存储在变量中
  • 结果是存储的值
第一步——计算左边变量的地址——是++的作用

这在C#规范中有明确的定义;有关详细信息,请参阅有关运算符优先级和执行顺序的部分

如果您对这个主题感兴趣,请参阅我关于优先级、关联性和顺序之间差异的大量文章:

如果您不了解++是如何工作的——不幸的是,几乎没有人了解它——请查看以下问题:

如果您不了解作业是如何工作的——我很惊讶地发现几乎没有人了解作业是如何工作的——请参见:

其他的答案指出,在C和C++编程语言中,语言规范没有指明如果副作用和观察结果在同一个“序列点”中出现了什么顺序的副作用,就像它们在这里一样。在C语言中,++的副作用可以在语句结束之前的任何时候发生。赋值后、赋值前,无论何时,由编译器自行决定。C#不允许这种格子。在C#中,在右边的代码执行时,观察到左边的副作用已经发生

此外,如果能链接到C#规范中指针类型的前置规则,我们将不胜感激。到目前为止找不到他们

所需的等级库部分为18.5,其中说明:

语法隐含了不安全运算符的优先级和关联性


因此,请阅读语法并将其解决。从阅读附录B第3节中的语法开始。

为什么不把它写成两行而不是一行呢?因为它不明显,也不容易阅读,为什么不把它分解成多个语句呢<代码>*p=*p-标量;p++
*p-=标量;p++
看起来不错吗?它给出了一个奇怪的结果,因为它是一个奇怪的语句。似乎是对的。分成几行将是最后一种存在。代码是更大的代码生成框架的输出。更改代价高昂(但当然可以做到)。没有人愿意提及的是,您可以使用
*p=*p++-scalar
*p++=*p2++-标量--第一个之所以有效,是因为副作用的C#顺序规则,第二个只是利用代码生成框架似乎设置的
p2
指针。(当然,除非后者用于您省略的其他内容)。“这有帮助吗?”语法暗示着“我以前才发现这个句子,希望得到一个更具体的短语?”?总之