5

この問題はしばらく私を悩ませました。NULL の別の定義を見たことがありません。

#define NULL  ((void *) 0)

NULL が異なる方法で定義されているアーキテクチャはありますか?もしそうなら、コンパイラがこれを宣言しないのはなぜですか?

4

4 に答える 4

4

C 2011 標準、オンライン ドラフト

6.3.2.3 ポインター
...
3 値が 0 の整数定数式、または型にキャストされたそのような式 は、ヌル ポインター定数void *と呼ばれます。66) null ポインター定数がポインター型に変換される場合、null ポインターと呼ばれる結果のポインターは、任意のオブジェクトまたは関数へのポインターと等しくないことが保証されます。
66) マクロNULL<stddef.h>(および他のヘッダーで) NULL ポインター定数として定義されます。7.19 を参照。

マクロ NULL常にゼロ値の定数式として定義されます。ネイキッド 0、または 0 へのキャスト、または 0void *に評価されるその他の整数式にすることができます。ソース コードに関する限り、NULLは常に 0 に評価されます。

コードが変換されると、ヌル ポインター定数 (0、NULLなど) が出現すると、基になるアーキテクチャがヌル ポインターに使用するものに置き換えられます。

于 2013-04-20T20:54:17.063 に答える
4

WhozCraigはこれらのコメントを現在削除された回答に書きましたが、完全な回答に昇格することができます (それが私がここで行ったことです)。彼は次のように述べています。

興味深い点: AS/400 は非常にユニークなプラットフォームであり、無効なポインターはすべて と同等と見なされNULLます。彼らがこれを行うために採用するメカニズムは、単に驚くべきものです。この意味での「有効」とは、既知の信頼できる命令セットによって取得された「値」を含む任意の 128 ビット ポインター (プラットフォームはすべてに 128 ビットの線形アドレス空間を使用します) です。信じられないかもしれint *p = (int *)1; if (p) { printf("foo"); }ませんが、そのプラットフォームでは「foo」は出力されません。p に割り当てられた値は、ソースが信頼されていないため、「無効」と見なされ、したがって と同等NULLです。

それがどのように機能するかは、率直に言って驚くべきことです。プロセスのマップされた仮想アドレス空間の各 16 バイトの段落には、プロセス全体のビットマップに対応する「ビット」があります。すべてのポインターは、これらの段落境界のいずれかに存在する必要があります。ビットが「点灯」している場合、対応するポインタは信頼できるソースから格納されています。それ以外の場合は無効であり、NULL と同等です。malloc、ポインター演算などの呼び出しは、そのビットが点灯するかどうかを判断する際にすべて精査されます。ご想像のとおり、ポインターを構造体に配置すると、構造体のパッキングという概念にまったく新しい世界がもたらされます。


これはコミュニティ ウィキとしてマークされています (これは私の回答ではありません。クレジットを取得するべきではありません)。ただし、WhozCraig が独自の回答を書いた場合は削除できます。

これが示しているのは、興味深いポインター プロパティを持つ実際のプラットフォームが存在するということです。

#define NULL ((void *)0)が通常の定義ではないプラットフォームがありました。一部のプラットフォームでは、コンパイラが理解できる限り、単に0、他のプラットフォーム、0Lまたはその他の適切な値にすることができます。C++ は定義として0ULL好きではありません。((void *)0)ヘッダーが C++ と連動するシステムでは、void ポインター バージョンを使用しない場合があります。

特定のメモリ位置のアドレスの表現が同じメモリ位置char *のアドレスとは異なるマシンで C を学びました。int *これは の前の日でしたが、適切に宣言void *する必要があり ( — プロトタイプもありません)、戻り値を正しい型に明示的にキャストするか、コア ダンプを取得する必要がありました。C 標準に感謝します (ただし、問題のマシンである ICL Perq (Three Rivers のバッジ付きハードウェア) は、標準が定義されるまでに大部分が取って代わられました)。malloc()char *malloc();

于 2013-04-20T20:13:20.513 に答える
1

ANSI-C が登場する前の暗黒時代、古い K&R C には、今日では奇妙と見なされる多くの異なる実装がハードウェア上にありました。これは、マシンが非常に「リアル」だった VM の時代よりも前のことです。ゼロのアドレスは、これらのマシンでうまくいっただけでなく、ゼロのアドレスが一般的である可能性があります...ゼロのシステム定数をゼロに格納することがあったのはCDCだと思います(これがゼロ以外に設定されている場合、奇妙なことが起こりました)。

if ( NULL != ptr ) /* このように */
 if ( ptr ) /* こんなの絶対に好きじゃない */

トリックは、メモリの最後に物を格納することも一般的であったため、「何もない」ことを示すために安全に使用できるアドレスを見つけることでした。これにより、一部のアーキテクチャでは 0xFFFF が除外されました。また、これらのアーキテクチャでは、バイト アドレスではなくワード アドレスを使用する傾向がありました。

于 2013-04-20T20:04:50.863 に答える
0

これに対する答えはわかりませんが、推測しています。C では通常、多くの malloc を実行し、その結果、返されたポインターに対して多くのテストを行います。malloc は void * を返し、特に失敗すると (void *)0 を返すため、malloc の成功をテストするために NULL を定義するのは自然なことです。これは非常に重要であるため、fopen のように、他のライブラリ関数も NULL (または (void *)0) を使用します。実際には、ポインタを返すものすべて。

したがって、これを言語レベルで定義する理由はありません。これは、非常に多くの関数によって返される可能性のある特別なポインター値にすぎません。

于 2013-04-20T20:02:12.740 に答える