如何处理c++代码中可能出现的整数溢出?
(How) do you handle possible integer overflows in C++ code?
不时地,特别是在做一些代码库的64位构建时,我注意到有很多情况下整数溢出是可能的。最常见的情况是我这样做:
// Creates a QPixmap out of some block of data; this function comes from library A
QPixmap createFromData( const char *data, unsigned int len );
const std::vector<char> buf = createScreenShot();
return createFromData( &buf[0], buf.size() ); // <-- warning here in 64bit builds
问题是,std::vector::size()
很好地返回size_t
(在64位构建中为8字节),但该函数恰好取unsigned int
(在64位构建中仍然只有4字节)。因此编译器会正确地发出警告。
如果可能的话,我尝试首先修复签名以使用正确的类型。然而,当我组合来自不同库的函数时,我经常遇到这个问题,我不能修改。不幸的是,我经常采用"好吧,没有人会做一个生成超过4GB数据的屏幕截图,所以为什么要这么做呢?"这样的推理,然后把代码改成
return createFromData( &buf[0], static_cast<unsigned int>( buf.size() ) );
使编译器关闭。然而,这感觉真的很邪恶。所以我一直在考虑有某种运行时断言,至少在调试构建中产生一个不错的错误,如:
assert( buf.size() < std::numeric_limits<unsigned int>::maximum() );
这已经好一点了,但是我想知道:你如何处理这种问题,即:整数溢出,这在实践中"几乎"是不可能的。我想这意味着它们不会发生在你身上,也不会发生在QA身上——但它们会在客户面前爆发。
如果您不能修复类型(因为您不能破坏库兼容性),并且您"确信"大小永远不会变得那么大,则可以使用boost::numeric_cast
代替static_cast
。如果值太大,将抛出异常。
当然,周围的代码必须对异常做一些模糊的明智的事情——因为它是一个"不期望发生"的条件,这可能意味着干净地关闭。
解决方案取决于上下文。在某些情况下,您知道数据在哪里来自,并且可以排除溢出:一个初始化的int
例如,0并每秒递增一次,不会溢出在机器使用寿命内的任何时间。在这种情况下,您只需转换即可(或者允许隐式转换完成它的工作),不用担心。
另一种类型的情况非常相似:例如,在您的情况中,它是可能不合理的屏幕截图有更多的数据,可以由int
表示,因此转换也是安全的。提供了数据确实来自于屏幕截图;在这种情况下,通常程序是对输入的数据进行验证,确保其满足要求您的约束下游,然后不做进一步的验证。
最后,如果你无法真正控制数据的来源,并且不能在进入时进行验证(至少不能对您的约束进行验证)下游),你被困在使用某种检查转换,在转换点立即验证
如果你将64位溢出的数字放入32位库中,你就打开了潘多拉的盒子——未定义的行为。
抛出异常。因为异常通常会在任何地方出现,所以你应该有合适的代码来捕获它。考虑到这一点,你不妨利用它。
错误消息令人不快,但它们比未定义的行为要好。
可以采用以下四种方式之一或组合使用:
- 使用正确的类型
- 使用静态断言
- 使用运行时断言
- 忽略直到受伤
最后,当静态断言不起作用时(就像在你的例子中),你使用运行时断言——是的,它们会出现在客户面前,但至少你的程序的行为是可预测的。是的,客户不喜欢断言—他们开始恐慌("我们有错误!"全大写),但是如果没有断言,程序可能会出错,并且没有办法轻松地诊断问题。
我想到了一件事:因为我需要某种运行时检查(例如buf.size()
的值是否超过unsigned int
的范围只能在运行时进行测试),但我不想到处都有一百万assert()
调用,我可以做一些像
template <typename T, typename U>
T integer_cast( U v ) {
assert( v < std::numeric_limits<T>::maximum() );
return static_cast<T>( v );
}
这样,我至少可以集中断言,并且
return createFromData( &buf[0], integer_cast<unsigned int>( buf.size() ) );
稍微好一点。也许我应该抛出一个异常(它确实非常异常!)而不是assert
,通过回滚以前的工作并发出诊断输出或类似的方法,给调用者一个优雅地处理这种情况的机会。
- 整数溢出,最大值为 pow(10,19)
- 模函数,避免C++整数溢出
- 优化正在杀死我在 clang 6 中的整数溢出检查
- 从双精度转换为整数的显式类型是否始终检查整数溢出?
- 运行时错误:有符号整数溢出:964632435 * 10 无法在类型 'int' 中表示
- 我很难理解这些关于检测整数溢出的评论
- 检测 32 位整数溢出
- 如何在没有整数溢出的情况下找到n%(k*k)
- C 中俄罗斯农民算法中的整数溢出
- 检查Android NDK COD中的整数溢出
- 对于特定情况的整数溢出似乎是由整数溢出引起的错误
- 如何修复整数溢出警告
- 整数溢出 C++ 即使使用“无符号长整型”也是如此
- 整数溢出是否会影响其他变量
- 为什么功能不给整数溢出
- 有符号整数溢出、内部函数和未定义的行为
- 整数溢出不会发生:它们从0重新启动
- 以下程序中最大 int 1000 000(在 int 范围内)的整数溢出
- 为什么Turbo C Wraparound每次签名的整数溢出,尽管签名的整数溢出是不确定的
- 编译器是否有可能检测整数溢出或其他数据类型溢出的可能性