C 为什么这些构造使用增量前后未定义的行为? #包括 内部主(空) { int i=0; i=i++++++i; printf(“%d\n”,i);//3 i=1; i=(i++); printf(“%d\n”,i);//2应该是1,不是吗? 挥发性INTU=0; u=u+++++u; printf(“%d\n”,u);//1 u=1; u=(u++); printf(“%d\n”,u);//2也应该是一,不是吗? 寄存器int v=0; v=v+++++v; printf(“%d\n”,v);//3(应与u相同?) int w=0; printf(“%d%d\n”,++w,w);//这不应该打印1吗 int x[2]={5,8},y=0; x[y]=y++; printf(“%d%d\n”,x[0],x[1]);//这不应该打印0 8或5 0吗? }

C 为什么这些构造使用增量前后未定义的行为? #包括 内部主(空) { int i=0; i=i++++++i; printf(“%d\n”,i);//3 i=1; i=(i++); printf(“%d\n”,i);//2应该是1,不是吗? 挥发性INTU=0; u=u+++++u; printf(“%d\n”,u);//1 u=1; u=(u++); printf(“%d\n”,u);//2也应该是一,不是吗? 寄存器int v=0; v=v+++++v; printf(“%d\n”,v);//3(应与u相同?) int w=0; printf(“%d%d\n”,++w,w);//这不应该打印1吗 int x[2]={5,8},y=0; x[y]=y++; printf(“%d%d\n”,x[0],x[1]);//这不应该打印0 8或5 0吗? },c,increment,undefined-behavior,operator-precedence,sequence-points,C,Increment,Undefined Behavior,Operator Precedence,Sequence Points,C有未定义行为的概念,即某些语言结构在语法上是有效的,但在代码运行时无法预测行为 据我所知,标准没有明确说明为什么存在未定义行为的概念。在我看来,这只是因为语言设计者希望在语义上有一定的回旋余地,而不是要求所有实现都以完全相同的方式处理整数溢出,这很可能会带来严重的性能成本,他们只是没有定义行为,因此如果编写导致整数溢出的代码,任何事情都可能发生 那么,考虑到这一点,为什么会出现这些“问题”?语言清楚地表明,某些事情会导致死亡。没有问题,没有“应该”的问题。如果当其中一个涉及的变量被声明为vol

C有未定义行为的概念,即某些语言结构在语法上是有效的,但在代码运行时无法预测行为

据我所知,标准没有明确说明为什么存在未定义行为的概念。在我看来,这只是因为语言设计者希望在语义上有一定的回旋余地,而不是要求所有实现都以完全相同的方式处理整数溢出,这很可能会带来严重的性能成本,他们只是没有定义行为,因此如果编写导致整数溢出的代码,任何事情都可能发生

那么,考虑到这一点,为什么会出现这些“问题”?语言清楚地表明,某些事情会导致死亡。没有问题,没有“应该”的问题。如果当其中一个涉及的变量被声明为volatile时,未定义的行为发生了变化,那么这并不能证明或改变任何事情。它是未定义的;你不能对这种行为进行推理

你最有趣的例子是

#include <stdio.h>

int main(void)
{
   int i = 0;
   i = i++ + ++i;
   printf("%d\n", i); // 3

   i = 1;
   i = (i++);
   printf("%d\n", i); // 2 Should be 1, no ?

   volatile int u = 0;
   u = u++ + ++u;
   printf("%d\n", u); // 1

   u = 1;
   u = (u++);
   printf("%d\n", u); // 2 Should also be one, no ?

   register int v = 0;
   v = v++ + ++v;
   printf("%d\n", v); // 3 (Should be the same as u ?)

   int w = 0;
   printf("%d %d\n", ++w, w); // shouldn't this print 1 1

   int x[2] = { 5, 8 }, y = 0;
   x[y] = y ++;
   printf("%d %d\n", x[0], x[1]); // shouldn't this print 0 8? or 5 0?
}

是一个未定义行为的教科书示例(请参阅Wikipedia上的条目)。

C有未定义行为的概念,即某些语言结构在语法上是有效的,但在代码运行时无法预测行为

据我所知,标准没有明确说明为什么存在未定义行为的概念。在我看来,这只是因为语言设计者希望在语义上有一定的回旋余地,而不是要求所有实现都以完全相同的方式处理整数溢出,这很可能会带来严重的性能成本,他们只是没有定义行为,因此如果编写导致整数溢出的代码,任何事情都可能发生

那么,考虑到这一点,为什么会出现这些“问题”?语言清楚地表明,某些事情会导致死亡。没有问题,没有“应该”的问题。如果当其中一个涉及的变量被声明为volatile时,未定义的行为发生了变化,那么这并不能证明或改变任何事情。它是未定义的;你不能对这种行为进行推理

你最有趣的例子是

#include <stdio.h>

int main(void)
{
   int i = 0;
   i = i++ + ++i;
   printf("%d\n", i); // 3

   i = 1;
   i = (i++);
   printf("%d\n", i); // 2 Should be 1, no ?

   volatile int u = 0;
   u = u++ + ++u;
   printf("%d\n", u); // 1

   u = 1;
   u = (u++);
   printf("%d\n", u); // 2 Should also be one, no ?

   register int v = 0;
   v = v++ + ++v;
   printf("%d\n", v); // 3 (Should be the same as u ?)

   int w = 0;
   printf("%d %d\n", ++w, w); // shouldn't this print 1 1

   int x[2] = { 5, 8 }, y = 0;
   x[y] = y ++;
   printf("%d %d\n", x[0], x[1]); // shouldn't this print 0 8? or 5 0?
}

是一个未定义行为的教科书示例(参见维基百科上的条目)。

我认为C99标准的相关部分是6.5表达式,§2

在上一个序列点和下一个序列点之间,对象应有其存储值 通过表达式的求值最多修改一次。此外,先验值 应为只读,以确定要存储的值

和6.5.16分配操作员,§4:

未指定操作数的求值顺序。如果试图修改 赋值运算符的结果或在下一个序列点之后访问它的结果 行为是未定义的


我认为C99标准的相关部分是6.5表达式,§2

在上一个序列点和下一个序列点之间,对象应有其存储值 通过表达式的求值最多修改一次。此外,先验值 应为只读,以确定要存储的值

和6.5.16分配操作员,§4:

未指定操作数的求值顺序。如果试图修改 赋值运算符的结果或在下一个序列点之后访问它的结果 行为是未定义的


只要编译和反汇编你的代码行,如果你很想知道它到底是什么,你就会得到你想要的

这是我在我的机器上得到的,以及我认为正在发生的事情:

u = (u++);
$cat evil.c
虚无罪恶{
int i=0;
i++=i+++++i;
}
$gcc evil.c-c-o evil.bin
$gdb.bin
(gdb)分解邪恶
函数邪恶的汇编程序代码转储:
0x00000000:推送%ebp
0x00000001:mov%esp,%ebp
0x00000003:子$0x10,%esp
0x00000006:movl$0x0,-0x4(%ebp)//i=0 i=0
0x0000000d:addl$0x1,-0x4(%ebp)//i++i=1
0x00000011:mov-0x4(%ebp),%eax//j=i=1 j=1
0x00000014:添加%eax,%eax//j+=j i=1 j=2
0x00000016:添加%eax,-0x4(%ebp)//i+=ji=3
0x00000019:addl$0x1,-0x4(%ebp)//i++i=4
0x0000001d:离开
0x0000001e:ret
汇编程序转储结束。

(我……假设0x00000014指令是某种编译器优化?

只要编译并反汇编您的代码行,如果您非常想知道它到底是什么,您就可以得到您想要的

这是我在我的机器上得到的,以及我认为正在发生的事情:

u = (u++);
$cat evil.c
虚无罪恶{
int i=0;
i++=i+++++i;
}
$gcc evil.c-c-o evil.bin
$gdb.bin
(gdb)分解邪恶
函数邪恶的汇编程序代码转储:
0x00000000:推送%ebp
0x00000001:mov%esp,%ebp
0x00000003:子$0x10,%esp
0x00000006:movl$0x0,-0x4(%ebp)//i=0 i=0
0x0000000d:addl$0x1,-0x4(%ebp)//i++i=1
0x00000011:mov-0x4(%ebp),%eax//j=i=1 j=1
0x00000014:添加%eax,%eax//j+=j i=1 j=2
0x00000016:添加%eax,-0x4(%ebp)//i+=ji=3
0x00000019:addl$0x1,-0x4(%ebp)//i++i=4
0x0000001d:离开
i = i++ + ++i;
i = ++i + 1;
a[i++] = i; 
i = i++ + ++i;
^   ^       ^

i = (i++);
^    ^

u = u++ + ++u;
^   ^       ^

u = (u++);
^    ^

v = v++ + ++v;
^   ^       ^
i = i++;
i = i++ + ++i;
while(*src++ = *dst++);
int k[] = {0,1,2,3,4,5,6,7,8,9,10};
int i = 0;
int num;
num = k[++i+k[++i]] + k[++i];
printf("%d", num);
num = k[i+1]+k[i+2] + k[i+3];
i += 3
i = i + 1
i++
i = i++
int i = 1;
i = i++;
il = ir++     // Note that suffix l and r are used for the sake of clarity.
              // Both il and ir represents the same object.  
temp = ir;      // i = 1
ir = ir + 1;    // i = 2   side effect by ++ before assignment
il = temp;      // i = 1   result is 1  
temp = ir;      // i = 1
il = temp;      // i = 1   side effect by assignment before ++
ir = ir + 1;    // i = 2   result is 2  
printf("%d %d\n", i, i++);
printf("%d %d\n", ++i, i++);
x = i++ + i++;
printf("%d %d\n", ++i, i++);
printf("%d %d\n", ++x, y++);
printf("%d %d\n", ++i, i++);
int i = 5;
int j;

j = (++i, i++);  // No undefined behaviour here because the comma operator 
                 // introduces a sequence point between '++i' and 'i++'

printf("i=%d j=%d\n",i, j); // prints: i=7 j=6
printf("%d %d\n", ++i, i++);
i = ++i + 1;
a[i++] = i;
i = i + 1;
a[i] = i;
% gcc plusplus.c -Wall -Werror -pedantic
plusplus.c: In function ‘main’:
plusplus.c:6:6: error: operation on ‘i’ may be undefined [-Werror=sequence-point]
    i = i++ + ++i;
    ~~^~~~~~~~~~~
plusplus.c:6:6: error: operation on ‘i’ may be undefined [-Werror=sequence-point]
plusplus.c:10:6: error: operation on ‘i’ may be undefined [-Werror=sequence-point]
    i = (i++);
    ~~^~~~~~~
plusplus.c:14:6: error: operation on ‘u’ may be undefined [-Werror=sequence-point]
    u = u++ + ++u;
    ~~^~~~~~~~~~~
plusplus.c:14:6: error: operation on ‘u’ may be undefined [-Werror=sequence-point]
plusplus.c:18:6: error: operation on ‘u’ may be undefined [-Werror=sequence-point]
    u = (u++);
    ~~^~~~~~~
plusplus.c:22:6: error: operation on ‘v’ may be undefined [-Werror=sequence-point]
    v = v++ + ++v;
    ~~^~~~~~~~~~~
plusplus.c:22:6: error: operation on ‘v’ may be undefined [-Werror=sequence-point]
cc1: all warnings being treated as errors
j = (i ++, ++ i);
i += 2;
j = i;
int i = 0;
printf("%d %d\n", i++, ++i, i);
#include<stdio.h>
int main(int argc, char ** argv)
{
int i = 0;
i = i++ + ++i;
printf("%d\n", i); // 2

i = 1;
i = (i++);
printf("%d\n", i); //1

volatile int u = 0;
u = u++ + ++u;
printf("%d\n", u); // 2

u = 1;
u = (u++);
printf("%d\n", u); //1

register int v = 0;
v = v++ + ++v;
printf("%d\n", v); //2
#include<stdio.h>
int main(int argc, char ** argv)
{
    int i = 0;
    //i = i++ + ++i;
    int r;
    r=i;
    i++;
    ++i;
    r+=i;
    i=r;
    printf("%d\n", i); // 2

    i = 1;
    //i = (i++);
    r=i;
    i++;
    i=r;
    printf("%d\n", i); // 1

    volatile int u = 0;
    //u = u++ + ++u;
    r=u;
    u++;
    ++u;
    r+=u;
    u=r;
    printf("%d\n", u); // 2

    u = 1;
    //u = (u++);
    r=u;
    u++;
    u=r;
    printf("%d\n", u); // 1

    register int v = 0;
    //v = v++ + ++v;
    r=v;
    v++;
    ++v;
    r+=v;
    v=r;
    printf("%d\n", v); //2
}
#include<stdio.h>
int main(int argc, char ** argv)
{
    int i = 0;
    i = i++ + ++i;
    printf("%d\n", i); // 3

    i = 1;
    i = (i++);
    printf("%d\n", i); // 2 

    volatile int u = 0;
    u = u++ + ++u;
    printf("%d\n", u); // 3

    u = 1;
    u = (u++);
    printf("%d\n", u); // 2 

    register int v = 0;
    v = v++ + ++v;
    printf("%d\n", v); // 3 
}
#include<stdio.h>
int main(int argc, char ** argv)
{
    int r;
    int i = 0;
    //i = i++ + ++i;
    ++i;
    r = i + i;
    i = r;
    i++;
    printf("%d\n", i); // 3

    i = 1;
    //i = (i++);
    r = i;
    i = r;
    i++;
    printf("%d\n", i); // 2 

    volatile int u = 0;
    //u = u++ + ++u;
    ++u;
    r = u + u;
    u = r;
    u++;
    printf("%d\n", u); // 3

    u = 1;
    //u = (u++);
    r = u;
    u = r;
    u++;
    printf("%d\n", u); // 2 

    register int v = 0;
    //v = v++ + ++v;
    ++v;
    r = v + v;
    v = r;
    v++;
    printf("%d\n", v); // 3 
}
i=i++
tmp = i
i=i+1
i = tmp
tmp = i
i = tmp
i=i+1
int x = 5;
printf("%d %d %d\n", x, ++x, x++);
int x = 5;
x = x++ + ++x;
printf("%d\n", x);
printf("%d %d %d\n", x, ++x, x++);
x = x++ + ++x;
y = x++;
z = x++ + y++;
x = x + 1;
x = a[i++];
x = a[i++] + b[j++];
x[i++] = a[j++] + b[k++];
x = *p++;
x = *p++ + *q++;
x = x++;
x = x++ + ++x;
y = x + x++;
a[i] = i++;
a[i++] = i;
printf("%d %d %d\n", x, ++x, x++);
x = x++ + ++x;
y = x + x++;