web-dev-qa-db-ja.com

Cで0へのポインタを間接参照する

メモリアドレス0x0のデータは非常に価値がある場合があります。よりよく知られている例としてx86リアルモードIVTを取り上げます。0x0から始まり、割り込みハンドラーへのポインターが含まれます。0x00のdwordは、ゼロ除算ハンドラーへのポインターです。

ただし、C11言語標準では、0で初期化されたポインターまたはnullポインターで初期化されたポインターとして定義されるnullポインター [WG14 N157 6.5.3.2]の逆参照は禁止されています [WG14 N157 6.3 .2.3]、最初のバイトを効果的に禁止します。

必要なときに実際に0x0をどのように使用しますか?

55
gfv

Cはnullポインタの逆参照を禁止せず、単に未定義の動作にします。

アドレス0x0を含むポインタを逆参照できるような環境の場合は、そうすることができるはずです。 C言語標準は、そうすると何が起こるかについては何も述べていません。 (ほとんどの環境では、結果としてプログラムがクラッシュします。)

具体的な例(これを正しく覚えている場合):68kベースのSun 3コンピューターでは、nullポインターを逆参照してもトラップは発生しませんでした。代わりに、OSはメモリアドレス0にゼロ値を格納し、nullポインタ(アドレス0を指す)を逆参照すると、そのゼロ値が生成されます。これは、たとえば、Cプログラムがnullポインタを空の文字列への有効なポインタであるかのように扱うことができることを意味しました。一部のソフトウェアは、意図的かどうかにかかわらず、この動作に依存していました。これには、ソフトウェアをSPARCベースのSun 4に移植するときに、大量のクリーンアップが必要でした。これにより、nullポインターの逆参照がトラップされました。 (これについて読んだことをはっきりと覚えていますが、参照を見つけることができませんでした。見つけられたら更新します。)

ヌルポインタは必ずしもアドレスゼロではないことに注意してください。より正確には、ヌルの表現はすべてビットゼロである場合とそうでない場合があります。非常に一般的ですが、保証されていません。 (そうでない場合、(void*)0の整数からポインターへの変換は簡単ではありません。)

comp.lang.c FAQ のセクション5では、nullポインタについて説明しています。

47
Keith Thompson

必要なときに実際に0x0をどのように使用しますか?

いずれかによって:

  • 必要なコードをアセンブリ言語で書く、または
  • cでコードを記述し、コンパイラが目的の操作に対して正しいアセンブリ言語を生成することを確認します
19
Greg Hewgill

ステートメント:

char * x = 0;

必ずしも0x0をxに入れるとは限りません。現在のアーキテクチャとコンパイラに対して定義されたnullポインタ値をxに入れます。

さて、実際には、一般的に使用されているすべてのコンパイラ/プロセッサは、そのステートメントに応答して、レジスタまたは格納場所に32(または64)0ビットを行に配置することになります。したがって、メモリアドレス0が有用である場合、次に、他の人が示しているように、正式に定義されていない動作を使用して立ち往生しています。しかし、昔々、「nullポインタ」がnotすべてゼロであるビットパターンであるハードウェアがありました。そして、誰が知っているか、再びあるかもしれません。

9
bmargulies

付録J次の場合は未定義の動作です...

単項*演算子のオペランドの値が無効です(6.5.3.2)。

あなたが言及した同じ脚注では、nullポインタは無効な値であると書かれています。したがって、それは禁止されていませんが、未定義の動作です。アドレス0x0とnullポインタの違いについては、 メモリアドレス0x0は使用可能ですか? を参照してください。

ヌルポインタは必ずしもアドレス0x0である必要はないため、アーキテクチャがヌルポインタを表す別のアドレスを選択する可能性があり、有効なアドレスとしてnewから0x0を取得する可能性があります。

ヌルポインタがオペレーティングシステムによって予約されているかC++実装によって予約されているかは指定されていませんが、プレーンnewは、アドレスが何であれ、ヌルポインタを返すことはありません(nothrow newは別の獣です)。だから、あなたの質問に答えるために:

メモリアドレス0x0は使用できますか?

たぶん、それは特定の実装/アーキテクチャに依存します。

つまり、システム上でクラッシュが発生しないことが確実な場合は、0x0を自由に使用してください。

2
user1508519

オペレーティングシステムは、ポインタのテーブルを使用してルーチンに割り込み、適切な割り込みを呼び出します。一般に(ほとんどのオペレーティングシステムでは)ポインタのテーブルは低メモリ(最初の数百程度の場所)に格納されます。これらの場所は、さまざまなデバイスの割り込みサービスルーチンのアドレスを保持します。

だからあなたがするとき

char *ptr = 0x0; 

次に、割り込みサービスルーチンのアドレスを使用してポインタを初期化している可能性があります。オペレーティングシステムに属するメモリ位置を逆参照(または変更)すると、プログラムがクラッシュする可能性があります。
したがって、0x0へのポインタを初期化せず、OSに属していないことが確認されるまで逆参照しない方がよいでしょう。

2
haccks