为什么这些构造(使用++)在C中的未定义行为?
#include <stdio.h>
int main(void)
{
int i = 0;
i = i++ + ++i;
printf("%dn", i); // 3
i = 1;
i = (i++);
printf("%dn", i); // 2 Should be 1, no ?
volatile int u = 0;
u = u++ + ++u;
printf("%dn", u); // 1
u = 1;
u = (u++);
printf("%dn", u); // 2 Should also be one, no ?
register int v = 0;
v = v++ + ++v;
printf("%dn", v); // 3 (Should be the same as u ?)
int w = 0;
printf("%d %d %dn", w++, ++w, w); // shouldn't this print 0 2 2
int x[2] = { 5, 8 }, y = 0;
x[y] = y ++;
printf("%d %dn", x[0], x[1]); // shouldn't this print 0 8? or 5 0?
}
C具有未定义行为的概念,即某些语言结构在语法上是有效的,但无法预测代码运行时的行为。
据我所知,该标准没有明确说明为什么存在未定义行为的概念。 在我看来,这只是因为语言设计者希望在语义上有一些余地,而不是要求所有实现都以完全相同的方式处理整数溢出,这很可能会带来严重的性能成本,他们只是留下行为未定义,所以如果你写代码导致整数溢出,任何事情都可能发生。
所以,考虑到这一点,为什么这些“问题”? 该语言清楚地表明,某些事情导致未定义的行为。 没有问题,没有“应该”涉及。 如果未定义的行为在其中一个涉及的变量被声明为volatile
发生改变,那么它不会证明或改变任何内容。 它是不确定的; 你无法推断行为。
你最有趣的例子,与
u = (u++);
是未定义行为的文本示例(请参阅维基百科关于顺序点的条目)。
只需编译和反汇编你的代码行,如果你很想知道你到底得到了什么。
这就是我在我的机器上得到的结果,以及我认为正在发生的事情:
$ cat evil.c
void evil(){
int i = 0;
i+= i++ + ++i;
}
$ gcc evil.c -c -o evil.bin
$ gdb evil.bin
(gdb) disassemble evil
Dump of assembler code for function evil:
0x00000000 <+0>: push %ebp
0x00000001 <+1>: mov %esp,%ebp
0x00000003 <+3>: sub $0x10,%esp
0x00000006 <+6>: movl $0x0,-0x4(%ebp) // i = 0 i = 0
0x0000000d <+13>: addl $0x1,-0x4(%ebp) // i++ i = 1
0x00000011 <+17>: mov -0x4(%ebp),%eax // j = i i = 1 j = 1
0x00000014 <+20>: add %eax,%eax // j += j i = 1 j = 2
0x00000016 <+22>: add %eax,-0x4(%ebp) // i += j i = 3
0x00000019 <+25>: addl $0x1,-0x4(%ebp) // i++ i = 4
0x0000001d <+29>: leave
0x0000001e <+30>: ret
End of assembler dump.
(我...假设0x00000014指令是某种编译器优化?)
我认为C99标准的相关部分是6.5表达式,§2
在前一个和下一个序列点之间,一个对象应该通过评估一个表达式最多修改其存储值一次。 此外,先验值只能读取以确定要存储的值。
和6.5.16作业操作员,§4:
操作数的评估顺序未指定。 如果尝试修改赋值运算符的结果或在下一个顺序点之后访问它,则行为是未定义的。
链接地址: http://www.djcxy.com/p/12733.html上一篇: Why are these constructs (using ++) undefined behavior in C?