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

use*_*238 6 c# pointers operator-precedence

在处理大型数组时,我正在进行不安全的指针计算,如下所示:

*c++ = *a++ - *b++; 
Run Code Online (Sandbox Code Playgroud)

它按预期工作.但对于就地操作,我也需要右侧的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(); 
}
Run Code Online (Sandbox Code Playgroud)

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

我发现了这个问题:在一个语句中取消引用和提前指针?.但它只处理右侧的*c ++表达式.为什么写访问与读访问不同?

此外,高度赞赏指向C#规范中指针类型的优先规则的链接.到目前为止找不到它们.

@EDIT:请注意,我们在这里讨论的是C#,而不是C或C++.即使我希望这里的差异不是太大.另外,就像上面的例子中一样,我知道,可以通过在下一个代码行中递增指针来防止这个问题.我想知道,为什么行为如上所述.

Eri*_*ert 15

关键是这句话就在这里:

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

这意味着您认为副作用的优先级和顺序是相关的.他们不是.副作用从左到右,时期,故事结束发生.如果你有

A().x = B() + C() * D();
Run Code Online (Sandbox Code Playgroud)

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

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

  • 评估左侧变量的地址
  • 评估右侧,如有必要,将其转换为变量的类型
  • 将值存储在变量中
  • 结果是存储的值

第一步 - 评估左侧变量的地址 - 是什么++.

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

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

http://blogs.msdn.com/b/ericlippert/archive/tags/precedence/

如果你不理解++的工作原理 - 不幸的是,几乎没有人这样做 - 看到这个问题:

i ++和++ 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节中的语法.