ヒープ・エラーは、メモリー管理関数がヒープの使用を制御するために使用する制御情報をユーザー・コードが不注意に上書きした場合に発生する可能性があります。 ヒープ内の割り振られたストレージの各ブロックは、割り振り関数によって戻されたアドレスから始まるデータ域と、そのデータ域に隣接する制御域 (ユーザーがストレージを割り振り解除したときにメモリー管理関数がそのストレージを適切に解放するのに必要な制御域) から構成されます。 ヒープ内の制御構造を上書きすると (例えば、配列に割り振られた境界の外側にあるエレメントに書き込んだり、あるいは割り振られたストレージ内の小さすぎるブロックにストリングをコピーした場合など)、制御情報が破壊されて、その他の割り振り済みブロックのデータ域が上書きされていなかったとしても、プログラムの誤った振る舞いの原因となる場合があります。
ヒープ・エラーを見つけようとするときには、以下の点を考慮してください。
ヒープ・エラーを検出するために、メモリー管理関数のヒープ・チェック・バージョンを使用するようにプログラムをコンパイルできます。 このオプションを指定してコンパイルしたプログラムを実行すると、メモリー管理関数を呼び出すたびにデフォルト・ヒープに対するヒープ・チェックが実行されます。 このヒープ・チェックでは、そのヒープ内のそれぞれの割り振り済みストレージ・ブロックの制御構造が検査され、どれも上書きされていないことが確認されます。 エラーが検出された場合は、そのプログラムは終了し、情報が標準エラーに書き込まれます。この情報には、ヒープ破壊が発生したアドレス、有効なヒープ状態が最後に検出されたソース・ファイルと行番号、およびそのメモリー・エラーが検出されたソース・ファイルと行番号が含まれています。
ヒープ・チェックは、各実行可能モジュールによって使用されるデフォルト・ヒープに対してのみ有効にされます。 メモリー管理関数のデバッグ・バージョンからはヒープの破壊が報告されていないが、それでも問題が疑われる場合は、追加ヒープを使用していて、それらを破壊している可能性があります。
エラーを起こしているヒープがデフォルト・ヒープであると分かっている場合は、そのヒープが有効であった最後の行と破壊が発生した最初の行との間のギャップを次第に狭めていくという方法で、デバッガー内からヒープ・エラーの原因を正確に特定できます。 実行コマンド、ステップ・コマンド、行ブレークポイントと関数ブレークポイント、 および「停止時にヒープ・チェックを実行 (Perform Heap Check on Stop)」 の設定を組み合わせて、検索の有効範囲を絞ります。 この設定については、下記の関連トピックを参照してください。
意味上の誤りがあるプログラムの場合は、「停止時にヒープ・チェックを実行」が邪魔をして、プログラムがスタック上のデータに対して誤ったアクセスを行うなどの異なる結果を引き起こすことがあります。 なぜなら、「停止時にヒープ・チェックを実行」によって、 デバッグ中のプロセスおよびスレッドは、実行が停止するたびにヒープ・チェック関数を呼び出し、 このヒープ・チェック関数がそのスタック・フレームでスタックの安全域の一部を上書きして、 この安全域に影響を与えるからです。 例えば、呼び出された関数があるローカル変数のアドレスを戻した場合、そのローカル変数の内容は呼び出し関数からアクセス可能で、呼び出された関数によって使用されたスタック・フレームが後続の呼び出しによって上書きされない限り変わりません。 しかし、「停止時にヒープ・チェックを実行」が使用可能になっているときに、その呼び出された関数からステップ・リターンを発行すると、呼び出された関数から戻されたときに即時にヒープ・チェック関数が呼び出されるため、戻されたポインターが指しているストレージがヒープ・チェック関数のスタック・フレームによって上書きされている場合があります。
デバッガー内でのヒープ・チェックは、ステップ・コマンドの場合、各ステップが終了するたびにヒープが検査されるため、オーバーヘッドが大きくなります。 コードの大きなセクションをステップスルーしているとき、あるいは頻繁に ブレークポイントで停止する場合に、デバッグ・パフォーマンスがあまりにも遅いときは、 ヒープ・エラーの原因となっている疑いのあるエリアでのみ 「停止時にヒープ・チェックを実行」を オンにしてみてください。