当我尝试使用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
更长。
为什么会得到这样的结果?任何帮助将不胜感激。
答案 0 :(得分:2)
size()
返回的值为std::size_t
,这是一个无符号整数类型。这意味着它只能表示非负数,并且如果您执行的运算结果为负数,则它将像模块化算术一样环绕到尽可能大的值。
在这里,2 - 1
为-1,在32位系统上,该字符串包装为2^32 - 1
。当尝试从10中减去2^32 - 1
时,由于32位整数的最小值为-2^31
,将导致有符号整数下溢。有符号整数上溢/下溢为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。
同样,有符号整数上溢/下溢是未定义的行为,因此,当发生某些奇怪的事情时,不要感到惊讶,尤其是对于现代编译器优化而言。例如,您的整个程序可以被“优化”,以使其完全不执行任何操作,因为它将始终调用UB。即使在该行执行之后调用了UB,您甚至不能保证看到std::cout<<m<<endl;
的输出。
答案 1 :(得分:0)
a.size()
返回的值是类型size_t
,它是一个无符号的int,因为没有任何理由使它的大小为负数。如果您对无符号数字执行1-2,则它将翻转并成为接近无符号int最大值的值,并且循环将需要相当长的一段时间才能运行,甚至可能因为有符号整数不能更大而停止比无符号值的上半部分大。这取决于比较签名和未签名的规则,我当时不确定该规则。
使用调试器并确保类型正确(您的编译器应在此处提及带符号/无符号不匹配)有助于确定这些情况。