每隔一段时间,特别是当做一些代码库的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中 - 但它们会在客户面前爆炸。
答案 0 :(得分:4)
如果您无法修复类型(因为您无法破坏库兼容性),并且您“确信”大小永远不会那么大,您可以使用boost::numeric_cast
来代替static_cast
。如果值太大,这将抛出异常。
当然,周围的代码必须做一些含糊不清的例外 - 因为这是一个“不期望永远不会发生”的情况,这可能只是意味着干净地关闭。仍然比继续错误的尺寸更好。
答案 1 :(得分:2)
解决方案取决于具体情况。在某些情况下,您知道数据的位置
来自,并且可以排除溢出:使用初始化的int
例如,0和每秒递增一次不会溢出
在机器的整个使用寿命期间。在这种情况下,你只需转换
(或允许隐式转换执行其中的操作),不要担心
关于它。
另一种情况非常类似:例如,在你的情况下,它是
可能不合理的屏幕schot有更多的数据可以
由int
表示,因此转换也是安全的。提供了
数据确实来自屏幕截图;在这种情况下,通常
程序是验证输入数据,确保它满足
您的约束下游,然后不再进一步验证。
最后,如果您无法真正控制数据的来源, 并且无法在输入时进行验证(至少不适用于您的约束 下游),你仍然坚持使用某种检查转换, 在转换点立即验证。
答案 2 :(得分:1)
如果将64位溢出数字推入32位库,则打开潘多拉盒子 - 未定义的行为。
抛出异常。由于异常通常可以在任何地方任意出现,你应该有适当的代码来捕获无论如何。鉴于此,你也可以利用它。
错误消息令人不快,但它们比未定义的行为更好。
答案 3 :(得分:0)
此类场景可以通过四种方式之一或使用它们的组合来保存:
通常最好的方法是使用正确的类型,直到你的代码变得丑陋然后滚动静态断言。 Static assertions are much better than runtime assertions for this very purpose.
最后,当静态断言不起作用时(如在您的示例中),您使用运行时断言 - 是的,它们进入客户的面孔,但至少您的程序行为可预测。是的,客户不喜欢断言 - 他们开始恐慌(“我们有错误!”所有上限),但如果没有断言,程序可能会行为不端,无法轻易诊断问题。
答案 4 :(得分:0)
我想到了一件事:因为我需要某种运行时检查(例如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
,以便让调用者有机会通过回滚以前的工作并发出诊断输出或者优先处理这种情况。等。