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

标签 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) {
        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(); 
}

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

我发现了这个 SO 问题:dereference and advance pointer in one statement? .但它只处理右侧的 *c++ 表达式。为什么写入访问权限与读取访问权限不同?

另外,非常感谢 C# 规范中指针类型优先规则的链接。到目前为止找不到他们。

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

最佳答案

关键是这里的这句话:

The pointer gets incremented before the dereference happens. This is correct according to precedence rules (++ before *). But now the new value is written to the incremented address, not to the original. Why is this so?

这意味着您相信副作用的优先级和顺序是相关的。 他们不是。副作用按从左到右、期间、故事结尾的顺序发生。如果你有

A().x = B() + C() * D();

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

在您的示例中,p++ 位于右侧 *p左侧,因此 的副作用code>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# 中,观察到左侧的副作用在右侧的代码执行时已经发生。

Also, a link to the preceedence rules for pointer types in the C# spec would be highly appreciated. Couldn't find them so far.

您想要的规范部分是 18.5,其中规定:

The precedence and associativity of the unsafe operators is implied by the grammar.

所以阅读语法并解决它。从阅读附录 B 第 3 节中的语法开始。

关于c# - 为什么 *p++ = *p - a 会给出奇怪的结果?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5538193/

相关文章:

c# - IMutableEntityType.GetProperties() 不包含我想要的属性

c - 如何将 sprintf/snprintf 与双指针 char 数组一起使用

c++ - 指针索引 : Getting Unexpected Values

c++ - 是 std::cin >> i >>++i;未定义的行为

c# - 报错不能在静态类中声明实例成员

c# - 正则表达式来测试逗号的正确使用

c# - 枚举类型的 CheckBoxList MVC Razor

c++ - 取消引用 NULL 指针是否被视为未指定或未定义的行为?

c++ - 运算符(operator)的评价顺序 *

c# - 运算符优先级