2

在处理 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

4 回答 4

3

正如其他人所指出的,这是由于 C++ 在比较具有不同符号的值时应用的有点违反直觉的规则。该标准要求编译器将这两个值都转换为 unsigned. unsigned出于这个原因,除非您进行位操作(实际数值无关紧要),否则通常认为最好避免这种做法。遗憾的是,标准容器没有遵循这个最佳实践。

如果您以某种方式知道向量的大小永远不会溢出 int,那么您可以将结果转换为std::vector<>::size()to int并完成它。然而,这并非没有危险。正如马克吐温所说:“杀死你的不是你不知道的事情,而是你确定的事情不是真的。” 如果插入向量时没有验证,那么更安全的测试是:

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

或者,如果您真的没有预料到这种情况,并且准备在它发生时中止,请设计(或找到)一个checked_cast函数并使用它。

于 2012-08-28T09:45:28.560 回答
1

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

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

这一切都完全合乎逻辑,只是不一定是您期望的逻辑。

例子...

$ 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
$
于 2012-08-28T04:30:47.227 回答
1

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

这就是您的示例中发生的情况。你rebuildIndex被隐式转换为vector<cv::Mat>::size_type. 即这个

rebuildIndex >= rebuiltFaces.size()

实际上被解释为

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

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

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

于 2012-08-28T05:45:46.050 回答
0

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

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

粗略地说,C++ 定义了这样的提升规则:任何类型的charorshort都首先提升为int,而不管符号性如何。比较中的较小类型在比较中提升为较大的类型。如果两种类型的大小相同,但一种是有符号的,一种是无符号的,则有符号的类型将转换为无符号的。这里发生的是您rebuildIndex正在转换为 unsigned size_t1被转换为1u0被转换为0u-1被转换为-1u,当转换为无符号类型时,它是类型的最大值size_t

于 2012-08-28T04:38:25.960 回答