【问题标题】:Would "p = p + (*p)++ * 3 + c;" cause an undefined behavior? [duplicate]将“p = p + (*p)++ * 3 + c;”导致未定义的行为? [复制]
【发布时间】:2012-10-10 16:02:12
【问题描述】:

可能重复:
Undefined Behavior and Sequence Points

根据标准中的定义,E1 += E2 与 E1 = E1 + E2 几乎相同,只是 E1 只计算一次。因此,此外,将 "p += (*p)++ + c";导致未定义的行为?

在 gcc/g++ (4.7 / 4.4) 中尝试以下代码。有 2 种结果:bxxxxx (g++4.7) 或 axbxxx (gcc, g++ 4.4)。如果我们在代码中执行(1)而不是(2),我们只能得到axbxxx。

#include <stdio.h>

int main() {
    char s[] = "axxxxx";
    char *p = s;

    printf("s = %s in the beginning.\n"
           "p is pointed at the %d-th char.\n", s, p - s);
    //p = p + (*p)++ * 3 + 2 - 'a' * 3; // (1)
    p += (*p)++ * 3 + 2 - 'a' * 3; // (2)
    printf("p is moved ahead by %d steps\n", p - s);
    printf("s = %s after the operation.\n", s);
    return 0;
}

我找不到它为什么会导致未定义的行为,也不能断言这是 gcc 的错误。

对于 axbxxx 结果,我也无法理解为什么操作数或后 ++ 会被计算两次(一次获取值,然后保存它)。由于在标准中说“1 ...被添加到它”,我认为地址应该只被评估一次。如果 post ++ 的操作数的地址只被计算一次,则表达式的效果将是相同的,不管赋值是按什么顺序执行的。

=== 更新 ===

阅读第一条评论中链接的文档后,我认为以下规则可能很重要:

"2) 此外,只能访问先前值以确定要存储的值。 .

那么,“p = p + (*p)++ * 3 + c”中p的访问是否会被认为是*p的“先验值”的一部分,与要存储的值无关在 *p?

IMO,没有违反这条规则。

【问题讨论】:

标签: c standards undefined-behavior


【解决方案1】:

不,p = p + (*p)++ * 3 + c 不会导致任何未定义的行为,假设 p 不指向 c

在这种情况下,有问题的部分是表达式内部*p 值的读取和修改。但是,读取该值是为了确定p 的新值(p 的新值与*p 中读取的值有直接的数据依赖性),因此它不违反要求。

我猜编译器中的错误实际上源于它在未指定情况下的不正确行为。请注意,该表达式有两个副作用:将新值存储在p 中并将新值存储在*p 中。未指定这些副作用发生的顺序。但是,在评估(*p)++ 子表达式期间,编译器应该“修复”++ 的特定左值参数,以确保新的(递增的)值存储在该确切对象中。看起来旧版本的编译器未能做到这一点,即首先评估 p 的新值,然后通过 @987654334 的 new 值存储 *p 的新值@。这显然是不正确的。

【讨论】:

    【解决方案2】:

    原则上,声明p += (*p)++ + c; 可能是正确的。它所做的只是将指针 (p) 提前某个值,而该值恰好由 p 指向的变量确定。

    您只需要确保您永远不会将p 增加超过s + 7。我没有仔细检查您的代码以查看是否是这种情况(但请注意,您正在做出某些编码连续性假设)。

    【讨论】:

      【解决方案3】:

      注意p += x; 不等同于p = p + x;,而是等同于p = p + (x);x 首先被评估,结果被添加到 p。在标题中给出的不带括号的公式中,p 的中间值可能指向数组之外,这确实是未定义的行为。只要x 的结果在数组内,代码中的版本就可以了。

      6.5.16.2.3 E1 op= E2 形式的复合赋值与简单赋值表达式 E1 = E1 op (E2) 的区别仅在于 左值 E1 只计算一次。

      J.2 未定义的行为 - 指针的加法或减法,或 就在后面,一个数组对象和一个整数类型会产生一个结果 不指向或仅超出同一数组对象 (6.5.6)。

      这个 UB 定义不限于赋值的最终结果。

      【讨论】:

        【解决方案4】:

        p += (*p)++ * 3 + 2 - 'a' * 3E1 = E1 + E2 形式。

        • 右侧有一个指针(地址变量)
        • 在左侧,您递增此指针所寻址的变量。

        编辑:发现p+=

        仍然没有未定义,因为无论E1 = E1 + E2 右侧的每个表达式的求值顺序如何,p 的值都是不变的。

        【讨论】:

        • OP海报参考E1 += E2的定义,标准定义为等同于E1 = E1 + E2
        • 谢谢我错过了这个区别
        猜你喜欢
        • 2017-12-16
        • 2014-12-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-11-17
        • 2018-03-16
        • 2013-08-14
        相关资源
        最近更新 更多