在for循环中奇怪的std::string::size()

4
该程序将在使用input.size() - 1作为for循环条件时打印“进入循环”。
std::string input;
input = {""};
int i = 0;
for (; i < input.size() - 1; ++i)
{
    cout << "Entered the loop" << endl;
}

然而,如果我将input.size() -1的值传递给一个整数(checksize):

std::string input;
input = {""};
int checksize = input.size() - 1;
int i = 0;
for (; i < checksize; ++i)
{
    cout << "Entered the loop" << endl;
}

如果程序不进入循环,则不会打印“已进入循环”的信息。

我在想为什么会这样?对我来说,这两段代码看起来是相同的。


没有编译器警告? - LogicStuff
1
大多数编译器默认关闭带符号-无符号警告,因为它会产生很多虚警。在第二个示例中,一些编译器会警告超出范围的赋值。 - M.M
这就是数学运算中使用有符号数字的原因。 - Barry
2个回答

7
你是无符号整数的受害者 :)
`std::string::size()`返回一个无符号整数(类型等同于`size_t`)。
当编译器计算 `input.size() - 1` 时,它会变成 `size_t(0) - 1`,由于计算使用无符号整数,所以你得到的不是-1而是一个非常大的整数(MSVC 32位编译器打印 `4294967295`,这对应于最大的32位无符号整数值 `2 ^ 32 - 1`)。
因此,这个循环:
for (int i = 0; i < input.size() - 1; ++i)

等同于:

for (int i = 0; i < /* very big positive number */; ++i)

这将会打印你的信息多次

然而,当你评估input.size() - 1并将其赋值给一个int变量(默认为signed)时,在第二种情况下,编译器仍然计算size_t(0) - 1作为一个非常大的正整数,但是随后该数字被转换为(signed)int,导致checksize被初始化为-1,因此您的循环不会执行:

for (int i = 0; i < checksize /* -1 */; ++i)

考虑以下可编译的代码:
#include <iostream>
#include <string>
using namespace std;

int main() 
{
    string input;

#ifdef CASE1
    for (int i = 0; i < input.size() - 1; ++i)
    {
        cout << "Entered the loop\n";
    }
#else
    cout << "input.size() - 1  == " << (input.size() - 1) << '\n';
    cout << "SIZE_MAX          == " << SIZE_MAX << '\n';

    int checkSize = input.size() - 1;
    cout << "checkSize == " << checkSize << '\n';

    for (int i = 0; i < checkSize; ++i)
    {
        cout << "Entered the loop\n";
    }
#endif
}

如果你使用MSVC编译CASE1并开启/W4选项(警告等级4,我强烈建议),你会在for循环条件中得到一个警告

cl /EHsc /W4 /nologo /DCASE1 test.cpp

test.cpp(10) : warning C4018: '<' : signed/unsigned mismatch

这通常表明您的代码存在问题。

相反,如果没有使用 CASE1 进行编译,则不会出现警告,并且以下输出结果显示 for 循环体从未执行:

cl /EHsc /W4 /nologo test.cpp

input.size() - 1  == 4294967295
SIZE_MAX          == 4294967295
checkSize == -1

2
input.size() - 1 是一个无符号计算,即使后来赋值为 int。编译器不会像这样计算 0 - 1;它将 4294967295 转换为 int(虽然不能保证给出相同的结果,但可能会)。 - M.M

2

input.size() 是一个无符号的量。当它为零时,减1会得到它所属类型的最大值(一个较大的正整数,可能是 SIZE_MAX)。

因此,进入循环是因为 0 < SIZE_MAX 为真。

但当你将这个较大的正整数转换为 int 时,这个数字超出了 int 的范围。然后发生了实现定义行为,可能会产生 checksize == -1。然后你的循环不会进入,因为 0 < -1 为假。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接