0

ユーザー空間プログラムの「データ」ポインターは、アドレス 0 および 0xffffffff から安全な距離にあると期待できますか? そのため、オーバーフローをチェックせずにポインターに小さなオフセットを安全に追加できますか? p が定数文字バッファーまたは動的に割り当てられた文字列 (最新の 32 ビット以上のオペレーティング システム) への char ポインターである場合、p + n がオーバーフローしないと安全に想定できる最大の正の n は何ですか?

混乱を避けるために:オーバーフロー チェックについて話しているのであって、境界チェックについてではありません。例: m 文字の文字列の先頭へのポインター p があり、正のオフセット i で文字にアクセスする場合、i < m を確認するか、間接的に p + i < を確認する必要があります。 p + m。ただし、後者の場合、p + i がオーバーフローしないことも確認する必要があります。つまり、p + i >= p であることを確認する必要があります。

更新: OK、p + i が実際に逆参照されているかどうか、またはオーバーフローしているかどうかに関係なく、i > m の場合、p + i は有効な標準 C ではありません。しかし、私が本当に興味を持っている問題は、実際には p + n がオーバーフローない小さな n があるかどうかです。この質問に答えるには、最新のオペレーティング システムがアドレス空間をどのように編成しているかについて、ある程度の知識が必要です。

Update2: たとえそれが一般化できないとしても、特定の 1 つのプラットフォームについて聞くことはすでに非常に興味深いことです。できれば、あいまいな組み込みのものではないことをお勧めします。x86 または Power ベースの 32 ビット Win、Linux、および Mac が最も興味深いでしょう。

4

8 に答える 8

8

ポインターに安全に追加 (および逆参照) できる唯一のオフセットは、作業中のメモリ ブロックにポインターを配置するオフセットです。このブロックは、new または malloc で割り当てられているか、スタック上に存在する必要があります。

いずれにせよ、メモリが存在することが保証されています (そして、メモリの終わりを過ぎたものは適切なアドレスです)。スタックに割り当てます。オーバーフローをチェックする必要はありません。

于 2009-04-05T09:42:59.987 に答える
4

厳密に言えば、答えは 0 です。これpは、既に配列の末尾を 1 つ過ぎたところを指している可能性があり、それが標準で有効であると述べられているためです。

特定の実装では、ある程度の量を回避できる場合がありますが、それは完全に実装定義です。CPU命令内のポインターの操作をチェックするハードウェアがあり、おそらく今もそうpです。2つのintの配列を指している場合、実行p+3するとCPUが命令に失敗します。一方、現在のほとんどのハードウェアでは、多くのことを回避できます。

于 2009-04-05T09:42:38.210 に答える
2

C では、ポインター演算と相対比較は「オブジェクト」内でのみ定義されます (C++ オブジェクトと混同しないでください)。「オブジェクト」とは、(任意の型の) 変数、または malloc/calloc/realloc で割り当てられたメモリ領域です。オブジェクトの「1 つ前」へのポインターを計算できます。これは、標準準拠の実装で常に機能します。

下位レベルで物事を見ると、通常、ポインターは (符号なし) 整数として実装されます。整数のサイズは、任意のメモリ位置のアドレスを保持するのに十分な大きさです。ポインターがオーバーフローする唯一の方法は、アドレス空間を超えた場合であり、C 標準に準拠している間は不可能です。

ただし、低レベルのコードを作成していて、ポインター演算がオブジェクト内でのみ有効であるという制限を無視している場合、これを行う最善の方法は、ポインターの表現方法に関する知識を利用することです。最近のほとんどの環境では、これは符号なし整数演算でオーバーフローをチェックするのと同じことになります。

(例外は、セグメント化されたメモリ アーキテクチャ、8086 または Multics のようなもの、およびおそらく、正気を保つために記憶から除外したその他のものです。)

于 2009-04-05T10:21:24.807 に答える
1

私の頭の上では、これは次のものに大きく依存しているようです。

  • OS
  • 基礎となるハードウェア
  • コンパイラ

原則として、私が覚えている限りでは、スタックは線形アドレス空間の一番上に割り当てられます。あらゆる種類のランタイム ライブラリが実行されていることを考えると、実際のデータはそのスペースの一番上で実行されていない可能性が高くなります。いずれにせよ、 によって割り当てられた領域を超えるmallocと別の問題が発生し、スタック フレームを超えると別の問題が発生します。要するに、0xffffffffffffffff から 0x0 へのラップアラウンドについて心配する必要はないと思いますが、静的、自動、また​​は手動で割り当てられたメモリの境界を超えないようにする必要があります。

于 2009-04-05T09:46:39.680 に答える
0

アドレス オーバーフローの場合、タイム キャンパーションに使用されていることがわかっている便利なトリックも、状況に応じて機能するはずです。

プラットフォームが 32 ビットの場合、各アドレスは 32 ビット幅 (unsigned long 型) であり、次のマクロを試すことができます。

#define address_after(a,b) ((long)(b) - (long)(a) < 0))

#define address_before(a,b) address_after(b,a)

|mi| < 0x7FFFFFFF (これは一般的な状況です)。このマクロは、オーバーフローの問題をうまく処理します。

于 2009-04-05T16:59:27.393 に答える
0

これは2つのことに依存しています

  • 建築学、建築物、建築様式
  • コンパイラ

そのため、確認する唯一の方法は、コンパイラとアーキテクチャのリファレンス ドキュメントを参照することです。

于 2009-04-05T09:43:23.450 に答える
0

通常、メモリ割り当てはブロック単位で行う必要があります。たとえば、1 バイトを使用する場合でも、最小割り当ては常に 2 の累乗でなければなりません。たとえば、MFC の CString は 128 バイトまたは 64 バイトを使用します。確かにいくらかのスペースを浪費する可能性がありますが、計算量も削減できます。ブロックに基づいて割り当てが行われた場合は、ブロックのサイズと現在のポインター値を使用して、オフセットの最大数を使用してオーバーフローを回避できます。

于 2009-04-05T09:51:45.723 に答える