web-dev-qa-db-ja.com

forループを使用したunsignedint逆反復

Forループのイテレータ変数がunsigned intとして0に逆反復するようにしたいのですが、i > -1の場合のように、signed intと同様の比較を考えることはできません。 。

for (unsigned int i = 10; i <= 10; --i) { ... }

しかし、これは、符号なし整数の数値オーバーフローが10を超えることに依存しているため、非常に不明確に思われます。

多分私は明確な頭を持っていませんが、これを行うためのより良い方法は何ですか...

免責事項:これは単純な使用例であり、上限10は簡単で、何でもかまいません。iunsigned intである必要があります。

43

あなたが使用することができます

for( unsigned int j = n; j-- > 0; ) { /*...*/ }

n-1から0まで繰り返します。

39
Serge Dundich

以下はあなたが望むことをします:

for (unsigned i = 10; i != static_cast<unsigned>(-1); --i)
{
    // ...
}

これは完全に定義されていますであり、実際に機能します。符号付きタイプの算術演算は、標準によって正確に定義されています。確かに:

4.7/2から(符号なし型へのキャストに関して):

宛先タイプが符号なしの場合、結果の値はソース整数と一致する最小の符号なし整数です(モジュロ2 ^ n、nは符号なしタイプを表すために使用されるビット数)

および.9.1/4

符号なしと宣言された符号なし整数は、2 ^ nを法とする算術の法則に従うものとします。ここで、nは、その特定のサイズの整数の値表現のビット数です。

23
Alexandre C.

std::numeric_limits<int>::max()より大きい数から本当に反復していますか?そうでない場合は、実際には、通常のintをループ変数として使用し、コード内の署名されていないことが予想される場所でstatic_castunsignedに使用することをお勧めします。このようにして、直感的な>= 0または> -1条件を使用できます。一般に、署名されていない他のどの条件よりも読みやすいと思います。

static_castは、変数の操作方法をコンパイラーに指示するだけであり、パフォーマンスへの影響はまったくありません。

3
Mark B

これに対する私のパターンは通常...

for( unsigned int i_plus_one = n; i_plus_one > 0; --i_plus_one )
{
    const unsigned int i = i_plus_one - 1;
    // ...
}
3
Adam Bowen

2つのオプションは、キャスト番号またはシンギング番号のいずれかであると考えることができます(たとえば、-1と比較して暗黙的に実行できます)。または、ループ条件を使用して、次のようにオーバーフローをチェックします。

for(unsigned i=10;i>i-1;--i){ } // i = 10, 9, ... , 1
for(unsigned i=10;i+1>i;--i){ } // i = 10, 9, ... , 1,0

このループは、iがオーバーフローするまで(つまり、ゼロに達するまで)続きます。 iが1で繰り返されることが重要であることに注意してください。そうしないと、無限ループになってしまう可能性があります。

2
Noam Weiss

私は2つの変数を使用します:

unsigned int start = 10;
for (unsigned int j = 0, i = start; j <= start; ++ j, -- i) {
    // ...
}

Whileループを使用することもできます。

unsigned int start = 10;
unsigned int i = start + 1;
while (i --) {
    // ...
}
1

私はc ++に不慣れで、私の答えは本当にばかげているかもしれませんが、私はこの種の逆ループに対してこれを行います。

size_t count = 3;
size_t newCount = 0;
if (count > 0)
    for (size_t i = count - 1; i >= newCount; i--)
    {
        //do your work here

        //and at the end
        if (i == 0)
            break;
    }

そしてそれは動作します。次のステップで実行されるループの「i--」部分なので、breakは問題なく機能するはずです。この方法は何が安全だと思いますか?

0
Ibrahim Ozdemir

iが1で繰り返される場合、オーバーフローを回避する簡単なトリックを次に示します。

for(unsigned int i = n-1; i+1 >= 1; i--) {;}

iを1回以上繰り返す場合は、

unsigned int d = 2;
for(unsigned int i = n-1; i+d >= d; i-=d) {;}
0
hmofrad

次のマクロを試して定義できます。

#define for_range(_type, _param, _A1, _B1) \
    for (_type _param = _A1, _finish = _B1,\
    _step = static_cast<_type>(2*(((int)_finish)>(int)_param)-1),\
    _stop = static_cast<_type>(((int)_finish)+(int)_step); _param != _stop; \
_param = static_cast<_type>(((int)_param)+(int)_step))

今、あなたはそれを使うことができます:

for_range (unsigned, i, 10,0)
{
    cout << "backwards i: " << i << endl;
}

これは、符号なし整数、列挙型、および文字を逆方向および順方向に繰り返すために使用できます。

for_range (char, c, 'z','a')
{
    cout << c << endl;
}

enum Count { zero, one, two, three }; 

for_range (Count, c, zero, three)
{
    cout << "forward: " << c << endl;
}

その厄介な定義にもかかわらず、それは非常にうまく最適化されています。 VC++の逆アセンブラを見ました。コードは非常に効率的です。延期しないでください。ただし、3つのforステートメント:コンパイラーは最適化後にループを1つだけ生成します。囲まれたループを定義することもできます。

unsigned p[4][5];

for_range (Count, i, zero,three)
    for_range(unsigned int, j, 4, 0)
    {   
        p[i][j] = static_cast<unsigned>(i)+j;
    }

明らかに、ギャップのある列挙型を反復処理することはできません。

0
Mikhail Semenov
for(unsigned i = x ; i != 0 ; i--){ ...

そして、i == 0のときにループ本体を実行し、その後停止したい場合。 i = x+1;から始めてください

ところで、なぜ私は署名されていない必要がありますか?

0
BenjaminB

アンダーフローの回避

unsigned int i = n;
while (i !=0){
--i;
...
}
0
halcon_cxx