该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
C99
i++ 值为 i,再让 i 自增 1;右值
++i 让 i 自增 1,用增 1 后的值作为表达式的值;右值
因为 i=i++; 在分号前没有序列点(序列点:该点之前对变量的各种影响应该完成,而后面的影响仍未发生。序列点能保证 ++i 等增 1 效果确实已经发生),所以 i 增 1 的效果不确定发生在何时。标准只规定发生在前一个序列点之后,后一个序列点之前。前面的 i 可能是增 1 后的 i,也可能是未增 1 后的。语句执行后 i 的值可能为 i 原值,也可能是原值+1,未定义。
Example 里提到过,表达式中何时增 1 是编译器相关的(只有 ; 产生了序列点,而相邻两序列点间有两次或以上的赋值,就是未定义),标准未定义行为。第 67 页备注里提到过,形如 i = ++i+ 1; a[i++] = i; 这样的语句都是标准未定义的。
C++2003
i++ 值为 i,再让 i 自增 1;右值,不可取址,优先级很高
++i 让 i 自增 1,用增 1 后的值作为表达式的值;左值,可取址(不满),优先级较高(比后缀自增运算符低)
对于 built-in 的类型,效果同 C99
对于非 built-in 类型,= ++ 是重载的,由于重载运算符实际上是函数,就会调用 operator= 函数,因为标准规定函数调用前和退出后后各有一个序列点。于是 i 的值将是调用 operator++ 函数后的值,并非未定义行为
★★★★★
对于 i=j=0; 对于 C99 标准并非未定义,而 C++2003 却是未定义的。关键在于,赋值运算符返回的结果,在 C99 中是右值,在 C++2003 中则是左值,作为赋值运算符的右操作数,左值需要转换为右值,导致对变量进行读取。然而,标准规定两个相邻序列点间的读取,不能是发生在修改之后的读取。i=j,这个j将进行重新读取,未定义。这样一来,C99 很多合法代码就成了未定义的,有很多人不满。
★★★★★
另外,
F(++i) //F 函数内部会引用 i
(a+=2) && (a+=1)
++a || --a
++a ? 2 : 1
a++, a
1。对于 built-in 类型
并非未定义行为,因为根据 C99 标准,&& || ?; , 左操作数求值有序列点,对变量的影响全部完成,后面如果引用到该变量,将是影响发生后的变量,并非未定义。C99:函数各参数全部求值后有一个序列点(库函数(非普通函数调用后也会有序列点);C++2003:函数调用前、退出后各有一个序列点。两种标准虽有差异,但都能保证 ++ 在对 i 产生影响后才会进入函数体
引用 C99 标准对逗号运算符的说明,译成中文:“逗号运算符的左操作数作为无类型表达式进行运算,它的运算有个顺序。然后右操作数进行运算。整个表达式的结果的类型和值都是右操作数的类型和值。如果尝试修改逗号表达式的结果,或者在下一个序列点存取(access)它,产生的行为是未定义的。”
2。对于非 built-in 类型,重载了 &&、|| 或 ,
显然 C语言 不支持重载,该情况仅在 C++ 中发生。因为重载后就成了函数,&& || , 不会产生短路语义,只在进出函数体时有序列点。所以,(a+=2) && (a+=1) 将是未定义的
★★★★★
i++ + ++i 这样的表达式就分析到这儿了,如果你们的卷子上出现这种题,你应该向老师指出