为什么在 for 循环中使用相同的条件会得到不同的结果?
why do I get different results with the same conditions in a for loop?
我在尝试使用 for
循环来解决问题时被卡住了。
这是我的简化代码:
int main(int argc, const char * argv[])
{
std::vector<int> a;
a.push_back(2333);
int n = 10, m = 10;
for(int i=0; i< -1; i++)
m--;
std::cout<<m<<endl;
for(int j=0; j<a.size()-2; j++)
n--;
std::cout<<n<<endl;
return 0;
}
显然,a.size() = 1
所以这两个结束条件应该是一样的。但是,当我 运行 我的代码在 Xcode 9.4.1 上时,我出乎意料,因为结果是 m = 10
和 n = 11
。而且我发现获取n
的值的时间比m
.
长很多
为什么我会得到这样的结果?任何帮助将不胜感激。
a.size()
返回的值是 size_t
类型,它是一个 unsigned int,因为没有任何理由让大小为负数。如果您使用无符号数执行 1-2,它将翻转并成为接近无符号 int 最大值的值,并且循环将花费相当长的时间 运行,或者甚至可能不会停止,因为有符号整数可以' 大于无符号值的上半部分。这取决于比较有符号和无符号的规则,我当场记不太清了。
使用调试器并确保类型正确(您的编译器应在此处提及 signed/unsigned 不匹配)有助于确定这些情况。
size()
返回的值为std::size_t
,是无符号整型。这意味着它只能表示非负数,如果您执行的运算结果为负数,它将像模运算一样循环到最大可能值。
这里,2 - 1
是-1,在32位系统上换行到2^32 - 1
。当您尝试从 10 中减去 2^32 - 1
时,会导致有符号整数下溢,因为 32 位整数的最小值为 -2^31
。有符号整数 overflow/underflow 是 undefined behavior,所以任何事情都有可能发生。
在这种情况下,下溢看起来就像一个带符号的整数一样环绕到最大值。所以结果将是 10 - (2^32 - 1) + 2^32
,即 11。我们添加 2^32
来模拟下溢回绕。换句话说,在循环的第 2^31 + 10
次迭代之后,n
是 32 位整数中可能的最小值。下一次迭代导致回绕,因此 n
现在是 2^31 - 1
。然后,剩余的 2^31 - 12
次迭代减少 n
到 11.
再次强调,有符号整数 overflow/underflow 是未定义的行为,因此当发生奇怪的事情时不要感到惊讶,尤其是在现代编译器优化的情况下。例如,您的整个程序可以 "optimized" 完全不做任何事情,因为它总是会调用 UB。您甚至不能保证看到 std::cout<<m<<endl;
的输出,即使在该行执行后调用了 UB。
我在尝试使用 for
循环来解决问题时被卡住了。
这是我的简化代码:
int main(int argc, const char * argv[])
{
std::vector<int> a;
a.push_back(2333);
int n = 10, m = 10;
for(int i=0; i< -1; i++)
m--;
std::cout<<m<<endl;
for(int j=0; j<a.size()-2; j++)
n--;
std::cout<<n<<endl;
return 0;
}
显然,a.size() = 1
所以这两个结束条件应该是一样的。但是,当我 运行 我的代码在 Xcode 9.4.1 上时,我出乎意料,因为结果是 m = 10
和 n = 11
。而且我发现获取n
的值的时间比m
.
为什么我会得到这样的结果?任何帮助将不胜感激。
a.size()
返回的值是 size_t
类型,它是一个 unsigned int,因为没有任何理由让大小为负数。如果您使用无符号数执行 1-2,它将翻转并成为接近无符号 int 最大值的值,并且循环将花费相当长的时间 运行,或者甚至可能不会停止,因为有符号整数可以' 大于无符号值的上半部分。这取决于比较有符号和无符号的规则,我当场记不太清了。
使用调试器并确保类型正确(您的编译器应在此处提及 signed/unsigned 不匹配)有助于确定这些情况。
size()
返回的值为std::size_t
,是无符号整型。这意味着它只能表示非负数,如果您执行的运算结果为负数,它将像模运算一样循环到最大可能值。
这里,2 - 1
是-1,在32位系统上换行到2^32 - 1
。当您尝试从 10 中减去 2^32 - 1
时,会导致有符号整数下溢,因为 32 位整数的最小值为 -2^31
。有符号整数 overflow/underflow 是 undefined behavior,所以任何事情都有可能发生。
在这种情况下,下溢看起来就像一个带符号的整数一样环绕到最大值。所以结果将是 10 - (2^32 - 1) + 2^32
,即 11。我们添加 2^32
来模拟下溢回绕。换句话说,在循环的第 2^31 + 10
次迭代之后,n
是 32 位整数中可能的最小值。下一次迭代导致回绕,因此 n
现在是 2^31 - 1
。然后,剩余的 2^31 - 12
次迭代减少 n
到 11.
再次强调,有符号整数 overflow/underflow 是未定义的行为,因此当发生奇怪的事情时不要感到惊讶,尤其是在现代编译器优化的情况下。例如,您的整个程序可以 "optimized" 完全不做任何事情,因为它总是会调用 UB。您甚至不能保证看到 std::cout<<m<<endl;
的输出,即使在该行执行后调用了 UB。