在阅读序列点后,我了解到i = ++i
未定义。
那么这段代码怎么样:
int i;
int *p = &i;
int *q = &i;
*p = ++(*q); // that should also be undefined right?
假设p和q的初始化取决于某些(复杂)条件。 他们可能像上面的情况一样指向同一个对象。 会发生什么?如果未定义,我们可以使用哪些工具进行检测?
编辑:如果两个指针不应指向同一个对象,我们可以使用C99限制吗? 这是“严格”的意思吗?
答案 0 :(得分:12)
是的,这是未定义的行为 - 您对对象进行了两次修改,它们之间没有序列点。不幸的是,自动检查是非常困难的 - 我能想到的最好的就是在此之前添加assert(p != q)
,这至少会给出一个干净的运行时故障,而不是更糟糕的事情。在一般情况下,在编译时检查这个是不可判定的。
答案 1 :(得分:4)
不检测的最佳工具,但首先要避免这种情况,是使用良好的编程习惯。避免副作用,每次作业只能写一次。
没有错*q += 1;
*p = *q;
答案 2 :(得分:2)
表达式与i = ++ i相同。可以检测到它的唯一工具就是你的头脑。在C中,权力是责任。
答案 3 :(得分:2)
除非另有说明,否则单个运算符的操作数和单个表达式的子表达式的评估顺序以及副作用发生的顺序是未指定的。 在上一个和下一个序列点之间,标量对象的表达式 最多只能修改一次存储值。此外,只能访问先前值以确定要存储的值。对于完整表达式的子表达式的每个允许排序,应满足本段的要求;否则行为未定义。
[ Example:
i = v[i ++]; / / the behavior is undefined
i = 7 , i++ , i ++; / / i becomes 9
i = ++ i + 1; / / the behavior is undefined
i = i + 1; / / the value of i is incremented
—end example ]
因此,这是未定义的行为:
int i;
int *p = &i;
int *q = &i;
*p = ++(*q); // Bad Line
在'Bad Line'中,标量对象'i'在表达式评估期间会多次更新。仅仅因为间接访问对象'i'不会改变规则。
答案 4 :(得分:1)
这是一个很好的问题。您突出显示的一件事是“序列点”,引用此site
为什么你不能依赖于以下表达式: a [i] = i ++; 因为没有为赋值,增量或索引运算符指定序列点,所以你不知道增量对i的影响何时发生。
此外,上面的表达式同样相同,因此行为未定义,对于跟踪该行为的工具为零,确定有splint将其命名为一个例子,但它是一个C标准,所以也许在我还没有听说过的工具中有一个隐藏的选项,也许Gimpel的PC Lint或Riverblade的视觉lint可能对你有帮助,尽管我承认它没有提到在这方面追踪未定义行为的任何事情。
顺便说一句,GCC的编译器版本4.3.3有这个选项-Wsequence-point
作为标记警告的一部分..这是在我的Slackware 13.0框上...
它只是表明,该代码可能看起来很好,并且编译得很好,但可能会导致后来的麻烦,最好的方法是进行代码审查,可以发现编译器可能不会选择的东西在那,这是最好的武器选择!