Why don't compilers make unsigned vs signed comparison safe?

时间:2015-10-31 00:14:33

标签: c++

As we know, code like this will generate an warning: for (int i = 0; i < v.size(); ++i) The solution is something like auto i = 0u;, decltype(v.size()) or std::vector<int>::size_type but pretend that we're forced to have both a signed and unsigned value. The compiler will automatically cast the int to be an unsigned int (the actual type doesn't matter). Using an explicit cast, static_cast<unsigned int>(i) makes the warning go away, but this is bad because it only did the same thing the compiler did and silenced an important warning! The better solution is: if ((i < 0) || (static_cast<unsigned int>(i) < v.size())) Understandably, C is "closer to the metal" and as a consequence more unsafe. But in C++, there's no excuse for this. As C++ and C diverge (as they have been doing for many years), hundreds of improvements to C++ have increased safety. I highly doubt a change like this would hurt performance either. Is there a reason why compilers don't do this automatically? N.B: this DOES happen in the real world. See Vulnerability Note VU#159523: This vulnerability in Adobe Flash arises because Flash passes a signed integer to calloc(). An attacker has control over this integer and can send negative numbers. Because calloc() takes size_t, which is unsigned, the negative number is converted to a very large number, which is generally too big to allocate, and as a result calloc() returns NULL causing the vulnerability to exist.

2 个答案:

答案 0 :(得分:1)

An important goal of C++ is compatibility, and there's a ton of code that would not compile if signed/unsigned mixing was a fatal error. See Stroustrup’s C++ Design Goals in 1986. Your proposal also adds a comparison that is not present in the source. Arguably with C++11 this case is already made more safe if you used ranged-for and auto: for (auto i : v)

答案 1 :(得分:1)

隐式转换的当前C ++规则使得无符号整数类型可用作 number 类型。典型的例子,string("hello").size() < -5"是有保证的,这是愚蠢的 - 但不是只是愚蠢。浪费时间追踪涉及此类转换的微妙错误,更不用说试图选择完美匹配类型所浪费的时间,这表明它比一个虚构的例子中的愚蠢行为更严重;它与编程一样严重。

一个糟糕的解决方案是通过添加额外的比较来容纳草率的程序员,这些程序员直接比较签名到无符号。这很糟糕,因为C ++建立在为有能力的程序员提供服务的基础上,他们知道他们正在做什么,并且希望他们的代码和最终的机器代码之间尽可能少。比较有符号到无符号在当前C ++中具有简单明确定义的效果,而不是例如Java程序员可能会期待。

一个好的解决方案是让程序员改进,直接表达他或她想要的东西,而不是依靠神奇的隐形修复。

例如,表达意图的一种好方法是将size操作的结果转换为签名类型,例如ptrdiff_t,或者当不需要完全普遍性时,只需int

支持这一点的一个好方法是定义一个n_items函数,如下所示:

using Size = ptrdiff_t;

template< class Container >
auto n_items( Container& c )
    -> Size
{ return c.size(); }

以不支持例如std::list可以使用end(c) - begin(c)使其适用于没有专业化的原始数组。但我更喜欢单独的(1)专业化。像这样:

template< class Item, Size n >
auto n_items( Item (&)[n] )
    -> Size
{ return n; }

免责声明:关闭袖口代码,不要被编制者的手接触。

1)从技术上讲,这是一个过载,而不是专业化。但我遵循标准化委员会前任秘书Pete Becker的术语负责人。即,让我们尽可能使用简单的描述性语言,并在必要时仅使用正式术语。