在溢出的情况下,stdint.h中定义的C99有符号整数类型是否表现出定义良好的行为?

在溢出的情况下,stdint.h中定义的C99有符号整数类型是否表现出定义良好的行为?,c,standards,c99,integer-overflow,C,Standards,C99,Integer Overflow,对于C中的“标准”有符号整数类型(short、int、long等)的所有操作,如果它们产生的结果超出[TYPE_MIN、TYPE_MAX]区间(其中TYPE_MIN、TYPE_MAX分别是最小和最大整数值),则会表现出未定义的行为。这可以由特定的整数类型存储 然而,根据C99标准,所有的intN\u t类型都必须具有2的补码表示: 7.8.11.1精确宽度整数类型 1.typedef name intN_t指定宽度为N的有符号整数类型,无填充 位和2的补码表示。因此,int8_t表示有符号整数

对于C中的“标准”有符号整数类型(short、int、long等)的所有操作,如果它们产生的结果超出[TYPE_MIN、TYPE_MAX]区间(其中TYPE_MIN、TYPE_MAX分别是最小和最大整数值),则会表现出未定义的行为。这可以由特定的整数类型存储

然而,根据C99标准,所有的
intN\u t
类型都必须具有2的补码表示:

7.8.11.1精确宽度整数类型
1.typedef name intN_t指定宽度为N的有符号整数类型,无填充 位和2的补码表示。因此,int8_t表示有符号整数 键入宽度正好为8位的字符

这是否意味着C99中的
intN\t
类型在整数溢出的情况下表现出定义良好的行为?例如,此代码定义良好吗

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>

int main(void)
{
    printf("Minimum 32-bit representable number: %" PRId32 "\n", INT32_MAX + 1);
    return 0;
}
#包括
#包括
#包括
内部主(空)
{
printf(“最小32位可表示数字:%”PRId32“\n”,INT32_MAX+1);
返回0;
}
不,没有

对类型范围内的值使用2的补码表示的要求并不意味着溢出行为的任何方面

中的类型只是现有类型的typedef(别名)。添加typedef不会改变类型的行为

第6.5节C标准第5段(C99和C11)仍然适用:

如果在评估过程中出现异常情况 表达式(即,如果结果不是数学定义的,或 不在其类型的可表示值范围内),行为 没有定义


这不会影响无符号类型,因为无符号操作不会溢出;它们被定义为产生包装结果,减少模类型_MAX+1。除了窄于
int
的无符号类型被提升为(有符号)
int
,因此可能会遇到相同的问题。例如:

unsigned short x = USHRT_MAX;
unsigned short y = USHRT_MAX;
unsigned short z = x * y;

如果
short
小于
int
(如果
short
int
分别为16位和32位,则
65535*65535
产生
4294836225
,超过
int\u MAX

尽管将超出范围的值存储到存储在内存中的有符号类型通常会存储该值的底部位,并且从内存重新加载该值会对其进行符号扩展,但许多编译器的优化可能会假设有符号算术不会溢出,并且溢出的影响在许多实际情况下可能是不可预测的例如,在使用一个32位累加器作为返回值(例如TMS3205X)的16位DSP上,
int16_t foo(int16_t bar){return bar+1;}
编译器可以自由地将
bar
加载到累加器中,扩展符号,添加一个累加器,然后返回,代码可能很好地将
z
设置为32768,而不是-32768。

这是在标准的某个地方隐式或显式提到的吗?我引用的6.5p5明确提到了它。标准中没有任何其他内容暗示6.5p5不适用于
intN\t
类型。标准中也没有任何内容定义Commonn 2的补码类型的概括行为;对于要定义的行为,标准必须在某个地方对其进行定义。“这不影响无符号类型,因为…”是否应将其限定为比
int
更窄的无符号类型,将其提升为
int
(而不是
无符号
)首先,然后他们遭受同样的行为限制?@BenVoigt:这个例子是否可以被更正为
longfoo(int-bar){returnbar+1;}
?我曾为DSP使用编译器,其中
int
为16位,但处理器的唯一32位累加器用于返回函数结果。如果这样一个函数由
long z=foo(32767)+1
调用,编译器可能会将
z
赋值为+32769,而不是-32767[调用代码将向累加器中添加一个,然后存储两个部分]。我不记得产生“有趣”行为的确切情况,但也有一些。@BenVoigt:另外,我发现您引用的语言很奇怪。
int16
表达式(包括实现定义的表达式)的所有可能的“值”都会吗是否需要一个可以存储在
int16
变量中而无需修改的变量?我发现有趣的是,超出范围的算法完全是“未定义的行为”,但超出范围的转换允许“实现定义的信号”@BenVoigt:另一个问题提到了一个更糟糕的例子,顺便说一句:给定
uint32_t x=0xFFFFFFFF
,它会
x=(x*x);
设置
x
为1还是会导致未定义的行为?会有什么不同吗?选项是什么?1.它被提升为自己的类型。然后结果是
((2**32-1)*(2**32-1)%%(2**32)
这是
-1
.2.它被提升为一个更大的有符号类型。然后结果是
((2**32-1)*(2**32-1))
如果结果是可表示的,否则UB.No,
x*=x
没有区别,除非
int
可以表示
((2**32-1)*(2**32-1))
,它将以
1
的形式存储在
x
中(因为对
无符号
类型的强制是使用模运算执行的)@BenVoigt:这就是为什么我把它作为一个讨厌的例子来提出来。人们不会期望在32位
int
类型的机器上工作的代码会在64位
int
类型的机器上导致溢出,但在上面的例子中它可以。