私はDSPチップ用のコンパイラーを使用しています。これは、そうではないCコードから配列の終わりを過ぎて1つにアクセスするコードを意図的に生成します!
これは、ループの構造が反復の終わりで次の反復のために一部のデータをプリフェッチするように構成されているためです。したがって、最後の反復の最後にプリフェッチされたデータは実際には使用されません。
このようなCコードを作成すると、未定義の動作が呼び出されますが、これは、最大限の移植性に関係する標準ドキュメントの形式にすぎません。
それよりも頻繁に、境界外にアクセスするプログラムは巧妙に最適化されていません。それは単にバギーです。コードはガベージ値をフェッチし、前述のコンパイラの最適化されたループとは異なり、コードはその後の計算でその値を使用するため、その値が破損します。
そのようなバグをキャッチする価値があるので、その理由だけでも動作を未定義にする価値があります。つまり、ランタイムが「main.cの42行目の配列オーバーラン」などの診断メッセージを生成できるようにします。
仮想メモリを備えたシステムでは、後に続くアドレスが仮想メモリのマップされていない領域にあるように、配列が割り当てられることがあります。アクセスにより、プログラムが攻撃されます。
余談ですが、Cでは配列の最後を過ぎたポインタを作成することが許可されていることに注意してください。そして、このポインターは、配列の内部へのどのポインターよりも大きく比較する必要があります。これは、Cの実装ではメモリの最後に配列を配置できないことを意味します。この場合、1つのプラスアドレスが折り返され、配列内の他のアドレスよりも小さく見えます。
それにもかかわらず、初期化されていない値または範囲外の値へのアクセスは、最大限の移植性がなくても、有効な最適化手法である場合があります。これは、たとえば、Valgrindツールが初期化されていないデータへのアクセスを報告しないのは、これらのアクセスが発生したときではなく、後でプログラムの結果に影響を与える可能性がある何らかの方法で値が使用された場合のみです。「xxx:nnnの条件付きブランチは初期化されていない値に依存しています」のような診断が表示され、発生元を追跡するのが難しい場合があります。このようなすべてのアクセスがすぐにトラップされると、コンパイラーによって最適化されたコードだけでなく、正しく手動で最適化されたコードからも多くの誤検知が発生します。
そういえば、Linuxに移植してValgrindで実行すると、これらのエラーが発生するベンダーのコーデックを使用していました。しかし、ベンダーは私にいくつかのビットだけが実際に使用されている値の一部は初期化されていないメモリからのものであり、それらのビットはロジックによって慎重に回避されました。値の適切なビットのみが使用されており、Valgrindには個々のビットを追跡する機能がありません。初期化されていない素材は、エンコードされたデータのビットストリームの終わりを超えて単語を読み取ることで得られましたが、コードはストリーム内のビット数を認識しており、実際よりも多くのビットを使用しません。ビットストリームアレイの末尾を超えてアクセスしても、DSPアーキテクチャに害を及ぼすことはありません(アレイの後に仮想メモリはなく、メモリマップされたポートはなく、アドレスはラップしません)。これは有効な最適化手法です。
ISO Cによると、C規格で定義されていないヘッダーを単に含めるか、プログラム自体またはC規格で定義されていない関数を呼び出すことが未定義の例であるため、「未定義の動作」はそれほど意味がありません動作。未定義の動作は、「地球上の誰によっても定義されていない」という意味ではなく、単に「ISO C標準によって定義されていない」という意味です。しかし、もちろん、時には未定義の動作が本当にされ、絶対に誰もが定義されていません。