12

ポインタに長さなどの値を追加し、この値を使用するコードをよく目にします。

T* end = buffer + bufferLen;//T* + size_t

if (p < end)

ただし、「buffer + bufferLen」がオーバーフローする可能性のあるメモリの終わり近くにバッファが割り当てられている可能性があります(たとえば、0xFFFFFFF0 + 0x10)。その結果、pが有効な要素アドレスであっても、「p<end」はfalseになります。 (例:0xFFFFFFF8)。

可能であれば、開始/終了範囲で機能する多くのものを見て、最後の要素の後に次の要素を終了する場合、どうすれば回避できますか?

4

4 に答える 4

9

標準から:

5.9関係演算子[expr.rel]

2つのポインターが同じ配列の要素を指している場合、または配列の終わりを超えている要素を指している場合、下付き文字が高いオブジェクトへのポインターはより高く比較されます。

したがって、心配する必要はありません。準拠した実装により、過去のポインタが配列の残りの部分と正しく比較されることが保証されます。加えて、

3.7.4.1割り当て関数[basic.stc.dynamic.allocation]

[...]返されるポインタは、基本的な配置要件(3.11)を持つ任意の完全なオブジェクトタイプのポインタに変換できるように適切に配置され、割り当てられたストレージ内のオブジェクトまたは配列にアクセスするために使用されます[.. 。]

つまり、返されたポインタは、適切なサイズの配列の先頭へのポインタとして扱うことができるはずなので、5.9は引き続き保持されます。これは、割り当て関数の呼び出しが呼び出しの結果である場合にoperator new[]当てはまります(5.3.4:5)。

実際問題として、アロケータが(不適合に)で終わるメモリのブロックを返すことが考えられるプラットフォームを使用している0xFFFFFFFF場合、ほとんどの場合、次のように書くことができます。

if (p != end)
于 2012-08-20T13:03:16.297 に答える
1

連続したメモリ割り当ての要素が連続していないアドレスを持つことはできません。end常により高い値のアドレスを持っていますstart

たとえば、割り当てが正確に0xFFFFFFFFで終了する場合、つまりend0x00000000になる場合、これはバグであり、そのシナリオに対応するようにコードを修正する必要があります。

一部のプラットフォームでは、このシナリオは設計上不可能であり、単純化のためにロジックの妥当な妥協点となる可能性があります。たとえば、私はif(p < end)Windowsユーザーモードアプリケーションで書くことを躊躇しません。

于 2012-08-20T12:57:21.067 に答える
1

確かに、多くの[start, end)ペアアルゴリズムのエンドポイントでは、最後の有効なエントリを超えています。ただし、実装は決して逆参照 endしないでください。実際にアクセスされる最後のエントリはである必要がありますend-1。これは、有効な領域にあることが保証されています。アルゴリズムが逆参照する*end場合はバグです。実際、有効なページの最後のバイトに意図的に領域を配置し、その直後に未割り当ての領域を配置するテストアロケータがあります。このようなアロケータでは、逆参照*endするアルゴリズムが保護違反を引き起こします。

FLG_HEAP_PAGE_ALLOCS

ページヒープのデバッグをオンにします。これにより、割り当てや解放などの動的なヒープメモリ操作が検証され、ヒープエラーが検出されるとデバッガが中断します。

このオプションは、イメージファイルに設定されている場合はフルページヒープのデバッグを有効にし、システムレジストリまたはカーネルモードに設定されている場合は標準のページヒープのデバッグを有効にします。

  • フルページヒープデバッグ(/ iの場合)は、割り当ての最後にアクセスできないページを配置します。

  • 標準のページヒープデバッグ(/rまたは/kの場合)は、割り当てが解放されるときに割り当てを調べます。

イメージファイルにこのフラグを設定することは、コマンドラインでイメージファイルにgflags / p enable/fullと入力することと同じです。

ポインタオーバーフローの問題については、VAアドレス0xFFFFFFFFを含むページを割り当てるオペレーティングシステムはありません。同様に、0x00000000を含むページを割り当てるオペレーティングシステムはありません。このようなオーバーフローが発生するためには、のサイズは、有効な範囲の終わりに予約されたすべてのVAを飛び越える*startのに十分な大きさである必要があります。start+1ただし、そのような場合、割り当てられたアドレスは、最後の有効なVAアドレスより少なくとも1つ小さいサイズである必要があります。これstart、有効であることを意味します(に割り当てられている限り、以下も常に有効です)。start+1start+Nstartsizeof(*start)*N

于 2012-08-20T13:02:20.637 に答える
-1

ご心配なく。あなたのアロケータ(おそらくnew、しかし多分何か他のもの)はあなたにそれが回り込むほどメモリの終わりに近い何かをあなたに与えません。

代わりに境界チェックについて心配してください。このようにラップアラウンドする割り当てを取得することはありません。配列をオーバーランしない限り(とにかく未定義の動作があります)、ラップアラウンドすることはありません。

プロセスアドレス空間の大きなチャンクがカーネル用に予約されていることに注意することも役立ちます。ほとんどのオペレーティングシステムでは、この上位領域は予約されています。

于 2012-08-20T12:57:07.177 に答える