我有一个复杂的定点运算程序。
似乎偶尔有溢出。
为了找到它们,我设置了标志-ftrapv
。这似乎仅适用于int32_t
。那是对的吗?
有没有办法与int16_t
和int8_t
实现相同的行为?
这是我的测试代码:
#include <stdio.h>
#include <stdint.h>
int main(void)
{
int8_t int8 = 127;
int8 += 1;
printf("int8: %d\n",int8);
int16_t int16 = 32767;
int16 += 1;
printf("int16: %d\n",int16);
int32_t int32 = 2147483647;
int32 += 1;
printf("int32: %d\n",int32);
}
我编译:
rm a.out; gcc -ftrapv main.c && ./a.out
并获得:
int8: -128
int16: -32768
Aborted
我的编译器版本是gcc (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
。
注意:有些答案涉及我之前写错误的测试程序。
答案 0 :(得分:6)
我不知道你要做什么。
int8_t
可容纳的最大值为127且 不 255. int16_t
的最大值为32767且 不 65535。int32_t
可以容纳的最大值确实是2147483647。这就是你的代码所做的事情:
int8_t int8 = 255;
将值255分配给最多可容纳127的变量。它不适合,以某种实现定义的方式调用隐式转换。最有可能的是你得到的值为-1。这是一个隐式左值转换,没有符号整数溢出。
printf("int8: %d\n",int8 + 1);
打印-1 + 1
的结果。它是0
。任何地方都没有溢出。
同样的事情发生在16位变量,隐式转换,最终值为-1,打印为0。
int32_t int32 = 2147483647;
此行与其他两行不同,因为您实际上将int32设置为它可以包含的最大值。如果你对它进行+1
,你会得到一个带符号的整数溢出,它会调用未定义的行为。
最重要的是,两个较小的整数类型可以在添加时溢出,即使这是你的代码正在做的事情(它没有做到)。两个操作数都会将整数提升为类型int
- 不会有溢出。有关其工作原理的详细说明,请参阅Implicit type promotion rules。
答案 1 :(得分:5)
我不这么认为,因为C的默认整数提升规则,算法确实不会发生在较小的类型中。
例如(来自C11草案,§5.1.2.311):
例2 在执行片段时
char c1, c2; /* ... */ c1 = c1 + c2;
“整数促销”要求抽象机推广 每个变量的值为
int
大小,然后添加两个int
和 截断总和。
这常常是代码混淆的原因,如:
uint8_t x;
x = get_some_byte();
x |= 1;
最后一行实际上相当于:
x = x | 1;
并且右侧将被提升为int
,因此“返回”uint8_t
的分配可能会截断某些工具警告您的内容。
答案 2 :(得分:-1)
我为自己找到了以下微创方法:
#include <stdio.h>
#include <stdint.h>
#ifdef INTOF_CHECK
#define INTOF_ERROR (printf("line %d: loss of bits\n", __LINE__) & 0)
#define i8(v) ((v) > 127 || (v) < -127 ? INTOF_ERROR : (v))
#define i16(v) ((v) > 32767 || (v) < -32768 ? INTOF_ERROR : (v))
#else
#define i8(v) (v)
#define i16(v) (v)
#endif
int main(void)
{
int8_t int8 = i8(127);
printf("int8: %d\n",i8(int8 + 1));
int16_t int16 = i16(32767);
printf("int16: %d\n",i16(int16 + 1));
}
编译:
rm -f a.out; gcc main.c -DINTOF_CHECK && ./a.out
输出结果为:
line 16: loss of bits
int8: 0
line 19: loss of bits
int16: 0