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

时间:2012-02-20 19:28:31

标签: c standards c99 integer-overflow

对于C(short,int,long等)中“标准”有符号整数类型的所有操作,如果它们产生的结果超出[TYPE_MIN,TYPE_MAX]区间(其中TYPE_MIN,TYPE_MAX是最小值,并且最大整数值,可以由特定的整数类型存储。

然而,根据C99标准,所有intN_t类型都需要具有二进制补码表示:

  

7.8.11.1精确宽度整数类型
  1. typedef名称intN_t指定有符号整数类型,其宽度为N,无填充   位和二进制补码表示。因此,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;
}

2 个答案:

答案 0 :(得分:12)

不,它没有。

对类型范围内的值的二进制补码表示的要求并不意味着溢出行为的任何内容。

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

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

  

如果在评估期间发生异常情况   表达式(即,如果结果未在数学上定义或   不在其类型的可表示值的范围内),行为   未定义。

这不会影响无符号类型,因为无符号运算不会溢出;它们被定义为产生包装结果,减少模 TYPE _MAX + 1.除了比int更窄的无符号类型被提升为(带符号)int,因此可以遇到同样的问题。例如,这个:

unsigned short x = USHRT_MAX;
unsigned short y = USHRT_MAX;
unsigned short z = x * y;
如果shortint窄,

会导致未定义的行为。 (如果shortint分别为16位和32位,则65535 * 65535会产生4294836225,超过INT_MAX。)

答案 1 :(得分:4)

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