我前几天正在阅读C标准,并注意到与有符号整数溢出(未定义)不同,无条件整数溢出已明确定义。我已经看到它在很多代码中用于最大化等等但是考虑到有关溢出的voodo,这被认为是很好的编程习惯吗?无论如何不安全吗?我知道许多像Python这样的现代语言不支持它 - 而是继续扩展大数字的大小。
答案 0 :(得分:20)
无符号整数溢出(环绕形状)通常在散列函数中得到充分利用,并且从年开始就是点。
答案 1 :(得分:3)
我能想到无符号整数溢出导致问题的一种方法是从一个小的无符号值中减去,从而导致它包装到一个大的正值。
答案 2 :(得分:3)
不久之后:
只要您注意并遵守定义(无论出于何种目的 - 优化,超级聪明的算法等),使用无符号整数溢出是完全合法/可靠/安全的。 I>
答案 3 :(得分:3)
仅仅因为您知道标准的细节并不意味着维护您的代码的人所做的。在稍后调试时,该人可能不得不浪费时间担心这一点,或者必须在以后查找标准以验证此行为。
当然,我们希望熟练掌握工作程序员语言的合理特征 - 不同的公司/团队对合理的熟练程度有不同的期望。但对于大多数团体来说,这似乎有点太多了,希望下一个人知道他/她的头顶而不必考虑它。
如果这还不够,当你在标准的边缘工作时,你更有可能遇到编译器错误。或者更糟糕的是,将此代码移植到新平台的人可能会遇到它们。
总之,我投票不要这样做!
答案 4 :(得分:2)
我一直用它来判断是否该做某事。
UInt32 now = GetCurrentTime()
if( now - then > 100 )
{
// do something
}
只要你检查'now'laps'然后'之前的值,就可以了解'now'和'then'的所有值。
编辑:我想这确实是一个下溢。答案 5 :(得分:1)
出于可读性原因,我不会依赖它。在确定将该变量重置为0之前,您将在几个小时内调试代码。
答案 6 :(得分:1)
另一个可以有用地使用无符号溢出的地方是当你必须从给定的无符号类型向后迭代时:
void DownFrom( unsigned n )
{
unsigned m;
for( m = n; m != (unsigned)-1; --m )
{
DoSomething( m );
}
}
其他替代方案并不那么整洁。除非你将m更改为signed,否则尝试执行m >= 0
不起作用,但是你可能会截断n的值 - 或者更糟 - 在初始化时将其转换为负数。
否则你必须做!= 0或> 0然后在循环后手动执行0个案例。
答案 7 :(得分:0)
由于CPU上的有符号数字可以用不同的方式表示,因此99.999%的当前CPU使用二进制补码表示法。由于这是大多数机器,因此很难找到不同的行为,尽管编译器可能会检查它(机会很大)。但是C规范必须占编译器的100%,所以没有定义它的行为。
所以它会让事情更加混乱,这是避免它的一个很好的理由。但是,如果你有一个非常好的理由(例如,代码关键部分的性能提升3倍),那么请将其记录下来并使用它。
答案 8 :(得分:0)
只要知道什么时候发生就可以依靠溢出......
例如,在迁移到更新的编译器时,我遇到了MD5的C实现问题... 代码确实期望溢出但它也预期32位整数。64位结果错误!
幸运的是,这就是自动化测试的目的:我很早就发现了问题,但如果不加注意的话,这可能是一个真正的恐怖故事。
你可以争辩说“但这种情况很少发生”:是的,但这就是让它变得更加危险的原因! 当有错误时,每个人都对过去几天写的代码持怀疑态度。 没有人怀疑“刚刚工作多年”的代码,通常没有人知道它是如何工作的......
答案 9 :(得分:0)
如果您明智地使用它(很好地评论和阅读),您可以通过使用更小更快的代码来从中受益。
答案 10 :(得分:0)
siukurnin提出了一个很好的观点,您需要知道何时会发生溢出。避免他描述的可移植性问题的最简单方法是使用stdint.h中的固定宽度整数类型。 uint32_t是所有平台和操作系统上的无符号32位整数,在为不同的系统编译时不会有不同的行为。
答案 11 :(得分:0)
我建议在任何时候都要依赖无符号数字换行时总是有一个明确的演员。否则可能会有惊喜。例如,如果“int”是64位,则代码如下:
UInt32 foo,bar;
if ((foo-bar) < 100) // Report if foo is between bar and bar+99, inclusive)
... do something
可能会失败,因为“foo”和“bar”会被提升为64位有符号整数。在检查结果100之前将类型转换回添加回UInt32可以防止出现问题。
顺便提一下,我认为直接获取两个UInt32产品的底部32位的唯一可移植方法是在进行乘法之前将其中一个int强制转换为UInt64。否则,UInt32可能会被转换为带符号的Int64,并且乘法会溢出并产生未定义的结果。