ANSI C89 / ISO C90のvoidポインターの境界
-
19-08-2019 - |
質問
ANSI C89 / ISO C90のvoidポインター値の上限と下限を移植可能に決定する方法はありますか?(現在、標準のコピーはありません(私は持っていますもちろん、voidポインター値が符号なしであることが保証されている場合、このタスクは(sizeof(void *)を介して)些細なことですが、これが保証されているかどうかを思い出すことはできません。アルゴリズム(オーバーフローするまでインクリメントなど)が、これらの境界を計算するための比較的安価な(時間の複雑さの点で)移植可能な方法があるかどうかを知りたいと思います。)
-編集-
また:ポインタ値の有効性を判断するポータブルな方法はありますか?
理由:これは同僚との話し合いの中で出てきて、私を困惑させました。彼が何に取り組んでいるのかわかりませんが、興味があるので知りたいだけです! :-)
解決
指定されたポインタが有効かどうかを判断する移植可能な方法はありません。あなたが扱っているメモリシステムの種類を知る必要があります。オペレーティングシステムとプロセッサに応じて、仮想メモリマネージャーのページテーブルを照会して、ポインターの有効範囲を決定する方法がある場合とない場合があります。
たとえば、Linuxでは、mmap
の下の特別な/proc
ファイルを調べて、プロセスの仮想メモリマップを取得できます。 cat
独自のメモリマップを読み出す例:
$ cat /proc/self/mmap 08048000-0804c000 r-xp 00000000 09:00 5128276 /bin/cat 0804c000-0804d000 rw-p 00003000 09:00 5128276 /bin/cat 0804d000-0806e000 rw-p 0804d000 00:00 0 [heap] f7ca7000-f7e40000 r--p 00000000 09:00 3409654 /usr/lib/locale/locale-archive f7e40000-f7e41000 rw-p f7e40000 00:00 0 f7e41000-f7f68000 r-xp 00000000 09:00 2654292 /lib/tls/i686/cmov/libc-2.3.6.so f7f68000-f7f6d000 r--p 00127000 09:00 2654292 /lib/tls/i686/cmov/libc-2.3.6.so f7f6d000-f7f6f000 rw-p 0012c000 09:00 2654292 /lib/tls/i686/cmov/libc-2.3.6.so f7f6f000-f7f72000 rw-p f7f6f000 00:00 0 f7f83000-f7f85000 rw-p f7f83000 00:00 0 f7f85000-f7f9a000 r-xp 00000000 09:00 2637871 /lib/ld-2.3.6.so f7f9a000-f7f9c000 rw-p 00014000 09:00 2637871 /lib/ld-2.3.6.so ff821000-ff836000 rw-p 7ffffffea000 00:00 0 [stack] ffffe000-fffff000 r-xp ffffe000 00:00 0 [vdso]
メモリが(r)eadable、(w)ritable、e(x)ecutable、または(p)resent(つまり、ディスクにページアウトされていない)かどうかを示すビットとともに、有効なポインターの範囲を確認できます。
他のヒント
ポインタは、仕様によって符号なしであることを保証されています。しかし、いったいなぜ境界を見つけたいのですか? <!> quot; 0x00000001と0xffffffff <!> quotの間のすべて有効なポインタの数はその小さなサブセットになるため、実際には有用なテストではありません。
void *は常にアドレス可能なメモリへのポインタを保持するのに十分な大きさです。その他の使用は、メジャーリーグ野球協会によって厳しく禁止されています。
例:dec-10は36ビットワードの36ビットアーキテクチャでした。ただし、アドレスは18ビットであり、任意のレジスタ/ワードに2つのポインターを保持できます。
はい-それは極端な例です。ポインターを使用して計算する必要がある場合、sizeofは有効です。しかし、連続した配列以外でポインター計算を行うことは、危険よりも危険です。
最後に-オブジェクトへのポインターまたはC ++のメンバーへのポインターを格納するために 'void *'を使用しないでください。多くのコンパイラー実装は、実際には複数の「物理」ポインターを使用して、具象(または部分的に具象)クラスの多重継承を実装します。この方法で複数の継承を使用する人はほとんどいないため、実際にはこれはほとんどありません。実際に発生した場合、何が起こったかを把握するのは非常に困難です。
メモリ内の実際のビットパターンからvoid *
をキャストできる整数値を識別する必要があります-sizeof(void *) == sizeof(long)
を整数型にキャストすると変換が必要になる場合があります!
void * p
を想定すると、<=>の場合、次は偽である可能性があります。
((long)p) == *((long *)&p)
また、標準では、すべての有効なポインターの値を保持するのに十分な大きさの整数型が であるかどうかも指定していません!
したがって、あなたがやりたいことをするポータブルな方法はありません...
NULLに対応する領域は別として、メモリアドレスには(ポータブルな)制限はまったくありません。十分に強化されたOSは、さまざまなCPU / OSメカニズムを利用して、malloc()の呼び出しごとに各プロセスにランダムで十分に分散されたアドレスを提供し、位置独立実行可能ファイルとASLRにより、任意のアドレスからコードを実行できます。
Win32では、64ビットポインターが符号拡張されることを知っています。拡張ポインターに署名しない場合、64ビットマシンから32ビットミニダンプを検査するのは楽しいです。
どのように64の-bitポインター(POINTER_64
)はWin32で動作します。