C言語での後方イテレーション
Iterating Backwards in C (davidpriver.com)
要約
C言語で配列やバッファを後方からイテレーションする際に、多くのプログラマーが誤った方法を採用しています。特にLLM時代においては、この問題はより顕著になっています。一般的な誤りは、符号付き整数型を使用したり、`i >= 0` のような条件でループを組むことですが、これらは未定義動作や予期せぬ挙動を引き起こす可能性があります。正しい方法は、`for(size_t i = count; i--; )` という構文を用いることです。
全文翻訳
C言語での後方イテレーション
これは他の投稿よりも短いですが、多くの人がこれを間違っているのを見かけます。LLM時代にはさらに悪化しています。
バッファとカウンターがあり、何らかの理由でそれを後方からイテレーションする必要があるとしましょう。
_Bool find_last(size_t count, int *haystack,
int needle, size_t *where);
ほとんどの人は、自動操縦で前方バージョンを実行できます。
for(size_t i = 0; i < count; i++){
if(haystack[i] == needle){
*where = i;
return 1;
}
}
return 0;
そこで、同じことを後方からイテレーションしようとします。
for(size_t i = count - 1; i >= 0; i--){
}
しかし、それは全くうまくいきません! i >= 0 はトートロジー的に真です(最新のコンパイラは警告を出します)。さらに悪いことに、count が 0 の場合、i はすぐに SIZE_MAX になります。
さて、次のバージョンを試します。
for(ssize_t i = count - 1; i >= 0; i--){
}
これは符号付きインデックスで実用的には機能しますが、いくつかの問題を引き起こします。
- インデックス変数が符号付きになり、Cの変換規則により式で使用する際に問題が発生する可能性があります。
ssize_tはPOSIXの機能なので、Windowsでは自分で定義する必要があります。- 型が32ビットの場合、プロモーションの問題が発生し、切り捨ての問題に遭遇する可能性もあります。
- このループのスペルは複雑すぎ、毎回特別な検査が必要です。2番目の句の条件は正しかったですか?
これらの問題を回避すると、C言語で後方イテレーションを行うための実際的で適切な方法が得られます。
for(size_t i = count; i--; ){
}
はるかにシンプルです!分解してみましょう:鍵は i の後置デクリメント演算子です。これは i をデクリメントしますが、操作前の i の値を返します。この場合、ループ本体では i は常に範囲内です。その値は初期化後にデクリメントされるため、最大で count - 1 になりますが、条件は以前が 0 であった場合にループが終了することを確認します。i はラップアラウンドしますが、符号なしのラップアラウンドはCで明確に定義されているため、問題ありません。
最終バージョンです。
_Bool find_last(size_t count, int *haystack,
int needle, size_t *where){
for(size_t i = count; i--; ){
if(haystack[i] == needle){
*where = i;
return 1;
}
}
return 0;
}
このようにすることで、符号付きループインデックスへの変換に手間をかけずに、ループがいかにシンプルであるかを見ることができます!誰かがリバースループを正しく実装したかどうかを一目で判断できます。
だからお願いです!誰かが後方イテレーションを試みているのを見かけたら、この方法を教えてあげてください!