为什么减法会溢出static_cast?

why does subtraction overflow with static_cast?

我理解 s1.size() - s2.size()s2 较大时下溢,因为它是 unsigned 的减法。 为什么将它们之一转换为 int 不会导致整数减法? 为什么铸造整个东西给我正确的结果?我希望它评估括号内的内容,然后下溢会给出一个大数字,然后转换为 int 不会有什么不同。我错过了什么?

#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;
}

当你这样做时

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();
}

恕我直言,这更容易阅读和理解。

将 "one of them" 转换为 int 后,您将进行混合 string::size_typeint 的算术运算。在这种混合中,无符号类型与 int 具有相同的等级或更高,这意味着无符号类型仍然 "wins":您的 int 被隐式转换回 string::size_type 并且计算在 string::size_type 的域中执行。您对 int 的转换实际上被忽略了。

同时,将结果转换为 int 意味着您正在尝试转换不适合 int 范围的值。这种情况下的行为是实现定义的。在现实生活中的 2's-complement 实现中,看到一个简单的表示截断并不罕见,它会产生 "correct" 结果。不过这不是一个好方法。

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

(理论上,您可以只将一个操作数转换为有符号类型,但为此您需要选择一种可以代表整个 string::size_type 范围的类型。)