2017-03-09 44 views
2

据我所知,当s2更大时,s1.size() - s2.size()下溢,因为它是unsigned的减法。 为什么将它们投射到int不会导致整数减法? 为什么要铸造整个事情给我正确的结果?我预计它会评估括号内的内容,然后下溢这会给出一个很大的数字,然后投到int不会有什么区别。我错过了什么?为什么减法与static_cast溢出?

#include <iostream> 
#include <string> 

using std::cout; 
using std::cin; 
using std::endl; 
using std::string; 

bool isShorter(const string &s1, const string &s2) { 
    return (static_cast<int>(s1.size()) - s2.size() < 0) ? true : false; // underflows 
    //return (static_cast<int>(s1.size() - s2.size()) < 0) ? true : false; // this works 

} 
int main() { 
    string s, t; 
    getline(cin, s); 
    getline(cin, t); 
    cout << "s: " << s << endl; 
    cout << "t: " << t << endl; 
    cout << "printing shorter string of the two..." << endl; 
    cout << ((isShorter(s, t)) ? s : t) << endl; 
} 
+3

你缺少规则说'signed + unsigned = unsigned'。 – nwp

+2

另外请注意,你可以做同样的事情,没有减法和铸造。 '返回((s1.size() NathanOliver

+2

@NathanOliver或只是'返回s1.size() WhozCraig

回答

3

当你

static_cast<int>(s1.size()) - s2.size() 

您转换s1.size()int,然后当你从它减去s2.size()int提升为同一类型s2.size(),然后被扣除。这意味着你仍然有无符号的整数相减,并且因为它永远不会是负数,它会绕回到一个更大的数字。这与s1.size() - s2.size()没有什么两样。

您有

static_cast<int>(s1.size() - s2.size()) 

可能出现的符号整数溢出的好处是不确定的行为同样的事情。如果s1小于s2,那么您仍然在执行无符号整数相减操作,因此比您绕回的数目要多。

你需要做的是将s1.size()s2.size()转换为一个有符号的整数类型来获得单式整数相减。这可能看起来像

static_cast<ptrdiff_t>(s1.size()) - static_cast<ptrdiff_t>(s2.size()) 

现在,你会真正得到一个负数,如果s1.size()小于s2.size()


应该指出,所有这些都可以通过使用少于运算符来避免。你的功能可以改写为

bool isShorter(const string &s1, const string &s2) 
{ 
    return s1.size() < s2.size(); 
} 

其中,恕我直言,是更容易阅读和理解。

2

将“其中之一”投射到int让算术运算混合了string::size_typeint。在此混合中,无符号类型与int或更高级别相同,这表示无符号类型仍为“胜出”:您的int隐式转换回string::size_type,计算在string::size_type的域中执行。您的转换为int被有效忽略。

同时,将结果投射到int意味着您正试图将不适合的值转换为int的范围。这种情况下的行为是实现定义的。在实际的二进制补码实现中,看到表示的简单截断并不常见,这会产生“正确”的结果。但这不是一个好方法。

如果要执行此减法作为有符号的减法,则必须将两个操作数转换为带符号的类型,确保目标带符号的类型可以表示这两个值。

(理论上,你可以逃脱只是一个操作数转换为符号的类型,但是对于你需要选择一个类型,它可以代表string::size_type的整个范围。)