c ++ illogical> =处理vector.size()时的比较很可能是由于size_type是无符号的

时间:2012-08-28 04:03:45

标签: c++ size-type

在处理vector.size()aka size_type

时,我可以用一些帮助来澄清这种奇怪的比较
vector<cv::Mat> rebuiltFaces;
int rebuildIndex = 1;
cout << "rebuiltFaces size is " << rebuiltFaces.size() << endl;

while( rebuildIndex >= rebuiltFaces.size() ) {
    cout << (rebuildIndex >= rebuiltFaces.size()) << " , " << rebuildIndex << " >= " << rebuiltFaces.size() << endl;
    --rebuildIndex;
}


我从控制台出来的是

rebuiltFaces size is 0
1 , 1 >= 0
1 , 0 >= 0
1 , -1 >= 0
1 , -2 >= 0
1 , -3 >= 0

如果我不得不猜测我会说编译器盲目地将rebuildIndex转换为unsigned和+ - 但是导致事情表现得很奇怪,但我真的不确定。有谁知道吗?

4 个答案:

答案 0 :(得分:3)

正如其他人所指出的,这是由于某种原因 反直觉规则C ++在比较不同的值时适用 符号类型;该标准要求编译器将两个值都转换为 unsigned。出于这个原因,它通常被认为是最佳实践 避免unsigned,除非你正在做一些操作(实际的 数值是无关紧要的)。遗憾的是,标准容器 不要遵循这个最佳实践。

如果你知道矢量的大小永远不会溢出 int,然后您可以将std::vector<>::size()的结果转换为 int并完成它。然而,这并非没有危险;作为马克 吐温说:“这不是你不知道杀死你的东西,而是你 我知道这不是真的。“如果没有验证的话 插入向量,然后更安全的测试将是:

while ( rebuildFaces.size() <= INT_MAX
        && rebuildIndex >= (int)rebuildFaces.size() )

或者,如果你真的不指望这种情况,并准备中止,如果它 发生,设计(或找到)checked_cast函数,然后使用它。

答案 1 :(得分:1)

在我能想到的任何现代计算机上,有符号整数表示为两个补码。 32位int max是0x7fffffff,int min是0x80000000,这使得当值为负时添加很容易。系统工作使得0xffffffff为-1,并且向其中加1会导致所有位翻转并且等于零。在硬件中实现它是非常有效的。

当数字从有符号值转换为无符号值时,存储在寄存器中的位不会改变。这使得一个几乎为-1的负值成为一个巨大的无符号数(unsigned max),如果内部的代码没有做一些会因为访问内存而导致程序崩溃的事情,这会使该循环运行很长时间。吨。

它完全合乎逻辑,不一定是你期望的逻辑。

示例...

$ cat foo.c
#include <stdio.h>

int main (int a, char** v) {
  unsigned int foo = 1;
  int bar = -1;

  if(foo < bar) printf("wat\n");
  return 0;
}

$ gcc -o foo foo.c
$ ./foo
wat
$

答案 2 :(得分:1)

在C和C ++语言中,当无符号类型具有与有符号类型相同或更大的宽度时,在无符号类型的域中执行混合有符号/无符号比较。被烧录的值被隐式转换为无符号类型。 “编译器”在这里“盲目地”做任何事都没什么。从一开始就像在C和C ++中那样。

这是你的例子中发生的事情。您的rebuildIndex已隐式转换为vector<cv::Mat>::size_type。即此

rebuildIndex >= rebuiltFaces.size()

实际上被解释为

(vector<cv::Mat>::size_type) rebuildIndex >= rebuiltFaces.size()

当有符号值转换为无符号类型时,转换是按照模运算的规则执行的,这是C和C ++中无符号算术背后的一个众所周知的基本原理。

同样,所有这些都是语言所要求的,它与数字在机器等中的表示方式完全无关,以及哪些位存储在哪里。

答案 3 :(得分:0)

无论底层表示(两个补码是最流行的,但是一个补码和符号幅度是其他),如果将-1转换为无符号类型,您将获得可以在该类型中表示的最大数字。

原因是无符号'溢出'行为被严格定义为通过模运算将值转换为0和该类型的最大值之间的数字。基本上,如果该值大于最大值,则重复减去最大值,直到您的值在范围内。如果您的值小于最小值(0),则重复添加最大值,直到它在范围内。因此,如果我们假设一个32位size_t,则从-1开始,小于0.因此,你添加2 ^ 32,给你2^32 - 1,这是在范围内,所以这是你的最终价值。

粗略地说,C ++定义了这样的促销规则:无论签名如何,任何类型的charshort都会首先提升为int。比较中的较小类型被提升到比较中的较大类型。如果两个类型的大小相同,但其中一个是有符号的,而另一个是无符号的,则签名类型将转换为无符号。这里发生的事情是,您的rebuildIndex正在转换为无符号size_t1转换为1u0转换为0u-1转换为-1u,转换为无符号类型时是size_t类型的最大值。