在溢出的情况下,i++是否对小于int的有符号类型调用未定义的行为?

Does i++ invoke undefined behavior for signed types smaller than int in case of overflow?

本文关键字:调用 类型 未定义 符号 小于 情况下 溢出 i++ 是否 int      更新时间:2023-10-16

很明显,由于算术溢出,下面的代码调用了未定义的行为:

#include <limits.h>
int test(void) {
    int i = INT_MAX;
    i++;  /* undefined behavior */
    return i;
}

但是小于int的符号类型,如shortsigned char呢?(较小的,我分别假设SCHAR_MAX < INT_MAXSHRT_MAX < INT_MAX)。

下面哪个函数调用了未定义的行为,为什么?

signed char test2(void) {
    signed char i = SCHAR_MAX;
    i = i + 1;   /* implementation defined */
    return i;
}
signed char test3(void) {
    signed char i = SCHAR_MAX;
    i += 1;   /* undefined behavior or implementation defined? */
    return i;
}
signed char test4(void) {
    signed char i = SCHAR_MAX;
    i++;      /* undefined behavior or implementation defined? */
    return i;
}
signed char test5(void) {
    signed char i = SCHAR_MAX;
    ++i;      /* undefined behavior or implementation defined? */
    return i;
}

请提供参考或引用C标准来支持你的推理。

对于+=和类似的操作符来说,直接对目标类型的值进行操作是合乎逻辑的,在许多实现中,它们实际上就是这样做的。然而,标准要求操作符的行为就好像目标类型的值经历了任何适用的标准和平衡提升,然后处理指定的操作,然后转换回目标类型。

因此,如果su是有符号和无符号的16位变量,int是32位,那么s*=s;将被定义为s的所有值[如果结果超过32767,它必须产生一个实现定义的值或引发一个实现定义的信号;大多数实现都必须自己去做除二补数缩减之外的任何事情。另一方面,u*=u;只保证在u的值不超过46340的情况下包装mod 65536;对于较大的值,它将调用未定义行为。

下面的代码会导致and overflow,从而导致未定义的行为:

signed char i = SCHAR_MAX;
i++; 

操作符后缀++不执行整数提升或通常的算术转换1。操作符只是在操作数2的值上加1。操作溢出

一个明显的区别来自一元算术运算符的措辞:+-~,它们的措辞清楚地表明操作数被提升为3。但是对于后缀++操作符,措辞并没有说该操作符被提升为4

显然后置++操作符不能提升操作数


1(引自:ISO/IEC 9899:201x 6.3.1.1布尔值、字符和整数2 58))
整数提升仅作为常规算术转换的一部分应用到certain参数表达式、一元+、-和~操作符的操作数,以及移位操作符,由它们各自的子句指定。

2(引自:ISO/IEC 9899:201x 6.5.2.4后置自增和自减运算符2)
作为一个副作用操作数对象的值递增(即适当类型的值1为)添加到它)

3(引自:ISO/IEC 9899:201x 6.5.3.3一元算术运算符2)
一元+运算符的结果是其(提升的)操作数的值。整数对操作数执行提升操作,结果具有提升后的类型。

4(引自:ISO/IEC 9899:201x 6.5.2.4后置自增和自减运算符2)
后缀++操作符的结果是操作数的值