2013-04-20 8 views
5

この問題はしばらく私に迷惑をかけました。私はNULLの異なる定義を見たことがない、それはNULLがdiferentlyに定義されている任意のアーキテクチャがあり、もしそうなら、なぜコンパイラは、私たちのためにこれを宣言しない なぜNULLがコンパイラによってあらかじめ定義されていないのですか

常に

#define NULL ((void *) 0) 
ですか?

+0

「NULL」ではなく「0」を使用できます。 – pmg

+0

NULLは 'stddef.h'で定義されていますので、自分で定義する必要はありません。 – Mat

+0

@pmg 0は、私の最初の質問に対する答えが真である場合に移植性がありません。 – stdcall

答えて

3

C 2011標準、online draft

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

マクロNULLは、ゼロ値の定数式として定義常にあります。コードがされたら、それは裸の0、またはvoid *から0キャスト、または限り、あなたソースコードとしてが懸念されて0に評価され、いくつかの他の整数表現することができ、NULLは常に0

と評価されますNULLポインター定数(0、NULLなど)の出現は、基になるアーキテクチャーがヌルポインターに使用するものと置き換えられます。ヌルポインターは、0値であってもなくてもかまいません。

0

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

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

1

ANSI-Cより前の暗い年代では、古いK & R Cには、今日は奇妙なものと考えられるハードウェア上で多くの異なる実装がありました。これはマシンが非常に「本当の」時代のVMの時代以前のことでした。 0のアドレスだけでなく、これらのマシンでうまくいきました。ゼロのアドレスが普及している可能性があります...私は時々ゼロのシステム定数を保存したのはCDCだと思います(そして、これがゼロ以外に設定されていれば)。

 if (NULL != ptr)  /* like this */ 
if (ptr)    /* never like this */

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

4

WhozCraigは、今削除されたanswerにこれらのコメントを書いていますが、完全な回答に昇格することができます(これは私がここで行ったことです)。彼はノート:

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

どのように動作するのは率直に驚くべきことです。プロセスのマップされた仮想アドレス空間内の各16バイトの段落は、プロセス全体のビットマップ内に対応する「ビット」を有する。すべてのポインタは、これらの段落の境界のいずれかに存在しなければなりません。ビットが「点灯」の場合、対応するポインタは信頼できるソースから格納され、そうでない場合は無効でNULLに相当します。 malloc、ポインタの数学などの呼び出しは、そのビットが点灯するかどうかを決定する際にすべて精査されます。あなたが想像しているように、ポインタを構造体に置くことは、構造体パッキングの考え方にまったく新しい世界をもたらします。


これはコミュニティのwikiをマークされている(それは私の答えではありません - 私はクレジットを取得するべきではありません)が、WhozCraigは彼自身の答えを書き込むと、それを削除することができます。

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

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

私は、与えられたメモリ位置のchar *アドレスの表現が同じメモリ位置のint *アドレスと異なるマシンでCを学習しました。これはvoid *の前の日でしたが、それはmalloc()が正しく宣言されていなければならないことを意味しました(char *malloc(); - プロトタイプもありません)。戻り値を正しい型に明示的にキャストするか、コアダンプがあります。 C標準には感謝しています(問題のマシンでは、ICL Perq - Three Riversのバッジ付きハードウェアは、標準が定義された時点までに大部分が置き換えられました)。

+2

私はこのまま滞在しても問題ありません。 OS/400のコードを書いてから9年が経ちましたが、それ以来変わってきたことは分かっていますが、これを達成した方法については非常にユニークな見解でした。プライムタイムには十分です。文字通りシステム・ジョブとしてスケジュールされているため、「DLL」関数のみが呼び出されました。そのプラットフォームはちょうど素晴らしい*です。 – WhozCraig

関連する問題