我目前正在编写一个程序,该程序大量使用uint64_t
类型,以增强跨平台的兼容性。
碰巧的是,程序中有很多溢出的可能性,所以我想使用built in overflow functions from gcc。
不幸的是,它们仅在int
,long int
,long long int
等上定义。 uintX_t
类型保证始终为X位大小,对于那些类型,例如long int
不能保证是64位。这让我觉得,内置的溢出功能不能在这里使用。
现在如何解决此问题?
我有两种方法:
使用UINT64_MAX
中的stdint.h
常量,自己进行溢出预测。但是,我不是“重新发明轮子”的朋友。
使用例如__builtin_add_overflow_p
函数仅检查溢出。但是,我不确定100%是否可以将它们应用于uint64_t
。
什么是最好的方法?我正在监督明显的事情吗?
答案 0 :(得分:4)
通过
uint64_t
使用内置的溢出功能
...以使用built in overflow functions from gcc。
要形成自己的builtin_uadd64_overflow()
,而又不“重新发明轮子”,请使用_Generic
来指导功能选择。
#define builtin_uadd64_overflow(a,b,r) _Generic(*(r), \
unsigned: __builtin_uadd_overflow, \
unsigned long: __builtin_uaddl_overflow, \
unsigned long long: __builtin_uaddll_overflow \
)(a,b,r)
答案 1 :(得分:1)
未签名的溢出是根据C标准(第6.2.5 / 9节)定义的行为
涉及无符号操作数的计算永远不能溢出,因为无法用所得的无符号整数类型表示的结果的模数要比该所得类型可以表示的最大值大一模。
因此,当您有类似的内容时,
uint64_t a, b, c;
c = a + b;
您可以通过测试检查操作是否溢出
int overflowed = c < a || c < b;
当然,您可以根据需要将其包装到函数中。
答案 2 :(得分:1)
以下功能对于int
或long long
类型的对象应该可以很好地工作;在32位平台上,int
函数将比使用long long
的方法高效得多;在64位平台上,long long
函数将与int
函数一样高效。
int safe_add1(int *value, int delta) {
int val = *value;
int sum = delta+(unsigned int)val;
if (((sum ^ val) & (sum ^ delta)) < 0)
return -1;
*value = sum;
return 0;
}
int safe_add2(long long *value, long long delta) {
long long val = *value;
long long sum = delta+(unsigned long long)val;
if (((sum ^ val) & (sum ^ delta)) < 0)
return -1;
*value = sum;
return 0;
}
请注意,要真正有效地防止溢出,在许多情况下,要求编译器使用整数溢出作为邀请来执行符合要求的扩展,该扩展将以足以满足程序要求的方式定义整数溢出语义,但会松散足以进行优化。例如,如果程序要在计算x+y > z
的同时确保不会检测到会产生数字错误行为的溢出,并且编译器知道x
和{{ 1}}相等,满足要求的最佳方法是计算z
,因为无论是否可以计算y > 0
都不会溢出,该行为在数值上是正确的
不幸的是,除非编译器提供扩展来识别这种结构,否则将无法编写特别有效的溢出检查代码。