11

何年もの間、私はforループ終了後にループ反復子の値を使用しないという習慣を身につけてきました。以前はコンパイラの警告が表示されていたので、これを実行したと断言することもできましたが、最近のコード レビューで異議を唱えられた後、間違いであることが証明されました。

たとえば、私はいつもこれを行っていました (注: 私たちのコード標準では、「break」キーワードの使用は禁止されています)。

int i, result;
bool done = false;
for (i=0; i<10 && !done; i++) {
    if (some_condition) {
        result = i;
        done = true;
    }
}
// Value of i may be undefined here

resultここで、 i の値に頼ることができれば、明らかに変数を削除できます。コンパイラの最適化のため、ループ反復子の値に依存できないと思いました。幻の教えを思い出しただけですか?それともこれが標準ですか(特にGNU Cに関して)?

4

5 に答える 5

12

C89C99、またはC11forでは、ステートメントの後に反復変数にアクセスすることは何も問題ありません。

 int i;

 for (i = 0; i < 10; i++) {
     /* Some code */
 }

 printf("%d\n", i);  // No magic, the value is 10

C99からはステートメントの最初の句として宣言も使用できますがfor、その場合、宣言された変数をステートメントの後に使用することはもちろんできませんfor

于 2012-05-01T15:13:06.247 に答える
4

異なる言語には異なるルールがあります。Pascal では、コンパイラは、最後のインクリメントの後にループ インデックスを格納することを最適化することが許可されているため最初のループ終了値または最後の有効な値 である可能性があります。

于 2014-04-24T17:39:04.117 に答える
2

for ループが反復子を進めるためだけに使用される使用例はたくさんあります。これは、strlen の一部の実装 (確かに strlen を実行する方法は他にもあります) や、特定の制限を見つけることを目的とする他​​の種類の関数で見られます。

/*find the index of the first element which is odd*/
for (ii = 0; ii < nelem && arry[ii] % 2 == 0; ii++);

前述のように、混乱のポイントは、イテレータ自体が for ステートメント内で定義されている構造から生じる可能性があります。

一般に、 for ステートメントは非常に強力ですが、残念なことに、通常、それらが最大限に活用されることはありません。

たとえば、同じループの別のバージョンは次のように記述できます (ただし、イテレータを使用することの安全性は示されません)。

#include <stdio.h>
int main(void)
{
    int cur, ii = 0, nelem, arry [] = { 1, 2, 4, 6, 8, 8, 3, 42, 45, 67 };
    int sum = 0;

    nelem = sizeof(arry) / sizeof(int);
    /* Look mom! no curly braces! */

    for (
            ii = 0;
            ii < nelem && ((cur = arry[ii]) %2 == 0 ||
                                ((printf("Found odd number: %d\n", cur)||1)));
            ii++, sum += cur
        );
    printf("Sum of all numbers is %d\n", sum);
    return 0;
}

この特定のケースでは、この特定の問題に対して多くの作業が必要に思えますが、いくつかの場合には非常に便利です。

于 2012-05-01T20:30:45.010 に答える
0

そのループの制御変数の値が適切に定義されていても、その変数のスコープが処理される方法と、特に履歴の処理が変更されたために、for ループの後でループの制御変数forを使用しないように言われた可能性があります。forC ++の(この質問に「C」というタグが付けられていることは知っていますが、ループの後にforループ制御変数を使用しないようにする根拠は、このC ++の歴史に由来する可能性があると思います)。

たとえば、次のコードを考えてみましょう。

int more_work_to_do(void) 
{
    return 1;
}

int some_condition(void)
{
    return 1;
}

int foo()
{
    int i = 100;

    while (more_work_to_do()) {
        int done = 0;

        for (int i = 0; i < 10 && !done; i++) {
            if (some_condition()) {
                done = 1;
            }
        }

        if (done) return i;   // which `i`?
    }

    return 1;
}

iループ内で宣言されたスコープの一部の古いルールではfor、コメント "which i" でマークされたステートメントで返される値は、ループによって決定されforます (VC++ 6 はこれらのルールを使用します)。その変数をスコープするための新しい標準ルールの下では、返される値はi関数の開始時に宣言されます。

于 2012-05-01T15:25:48.940 に答える
0

あなたの習慣がどのようになったのかはわかりませんが、同じことをする私の習慣がどのようになったかは言えます。次のようなコードを見ることでした。

for (i=0u; (i<someLimit) && (found != TRUE); i++)
{
    if (someCondition) found = TRUE;
}
foundIndex = i-1;

基本的に、このようなコードは、たとえば MISRA に基づくなど、一部のコーディング ルールで break キーワードが許可されていない場合に記述されます。ただし、ループから抜け出さないと、ループは通常、必要なものから 1 つずれている「i」を残します。

時々、これを見つけることさえできます:

for (i=0u; (i<someLimit) && (found != TRUE); i++)
{
    if (someCondition) found = TRUE;
}
foundIndex = i;

これは単に意味的に間違っており、単体テストで十分にカバーされていない既存のコード ベースに「ブレーク キーワードを禁止する規則」が導入された場合に見つかります。意外に聞こえるかもしれませんが、すべてがそこにあります...

于 2015-08-19T08:52:55.433 に答える