2

vector.size() 別名 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ビット操作(実際の数値が関係ない場合)を行わない限り、回避することが一般的にベストプラクティスと見なされます。残念ながら、標準のコンテナはこのベストプラクティスに従っていません。

ベクトルのサイズがオーバーフローすることは決してないことがどういうわけかわかっている場合は 、 to intの結果をキャストして、それで済ませることができます。ただし、これには危険が伴います。マーク・トウェインが言ったように、「あなたを殺すのはあなたが知らないことではありません、それはあなたが確かに真実ではないことを知っていることです。」ベクトルに挿入するときに検証がない場合、より安全なテストは次のようになります。std::vector<>::size()int

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

または、実際にケースを予期せず、発生した場合に中止する準備ができている場合は、checked_cast関数を設計(または検索)して使用します。

于 2012-08-28T09:45:28.560 に答える
1

私が考えることができる最新のコンピューターでは、符号付き整数は 2 の補数として表されます。32 ビットの int max は 0x7fffffff、int min は 0x80000000 です。これにより、値が負の場合に加算が容易になります。システムは 0xffffffff が -1 になるように動作し、これに 1 を追加すると、ビットがすべてロールオーバーしてゼロになります。ハードウェアに実装するのは非常に効率的です。

数値が符号付きの値から符号なしの値にキャストされる場合、レジスタに格納されているビットは変更されません。これにより、-1 のようなわずかに負の値が巨大な符号なし数値 (符号なし最大値) になり、内部のコードがメモリにアクセスしてプログラムをクラッシュさせるようなことをしなければ、ループが長時間実行されることになります。 t。

すべて完全に論理的ですが、必ずしも期待した論理とは限りません。

例...

$ 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++ 言語では、符号なし型の幅が符号付き型と同じかそれより大きい場合、符号付き/符号なしの混合比較が符号なし型のドメインで実行されます。署名された値は暗黙的に符号なしの型に変換されます。ここで「コンパイラ」が「やみくもに」何かをすることについては何もありません。C や C++ では、最初からそうでした。

これがあなたの例で起こることです。YourrebuildIndexは暗黙的に に変換され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

基になる表現 (2 の補数が最も一般的ですが、1 の補数と符号の大きさは別です) に関係なく、符号なしの型に -1 をキャストすると、その型で表現できる最大の数が得られます。

その理由は、符号なしの「オーバーフロー」動作が、モジュロ演算によって値を 0 とその型の最大値の間の数値に変換するものとして厳密に定義されているためです。基本的に、値が最大値よりも大きい場合は、値が範囲内になるまで最大値を繰り返し減算します。値が最小値 (0) より小さい場合は、範囲内になるまで最大値を繰り返し追加します。したがって、32 ビットを想定すると、size_t0 未満の -1 から開始します。したがって、2^32 を追加する2^32 - 1と、範囲内の が得られ、それが最終的な値になります。

大雑把に言えば、C++ は昇格規則を次のように定義します。符号の有無に関係なく、任意のタイプのcharorshortが最初に に昇格されintます。比較の小さい型は、比較の大きい型に昇格されます。2 つの型のサイズが同じで、一方が符号付きでもう一方が符号なしの場合、符号付きの型は符号なしに変換されます。ここで起こっているのは、あなたrebuildIndexが unsigned に変換されているということsize_tです。1は に変換され1u0は に変換され0u、は に-1変換されます。-1u符号なしの型にキャストすると、型の最大値になりsize_tます。

于 2012-08-28T04:38:25.960 に答える