C標準では、マシンのアドレス0にnullポインターを置く必要はありません。ただし、0
定数をポインター値にキャストすると、NULL
ポインター(§6.3.2.3/ 3)が生成され、nullポインターをブール値として評価するとfalseになる必要があります。本当にゼロアドレスが必要で、ゼロアドレスでNULL
はない場合、これは少し扱いにくい場合があります。
それでも、コンパイラと標準ライブラリに(大きな)変更を加えたため、標準ライブラリにNULL
厳密に準拠したまま、代替ビットパターンで表現することは不可能ではありません。しかし、真と評価されるように、それ自体の定義を単に変更するだけでは十分ではありません。NULL
NULL
具体的には、次のことを行う必要があります。
- ポインタへの代入(またはポインタへのキャスト)でリテラルのゼロを、などの他の魔法の値に変換するように調整します
-1
。
0
代わりに、マジック値をチェックするために、ポインターと定数整数の間の等価テストを調整します(§6.5.9/ 6)
- ポインター型がブール値として評価されるすべてのコンテキストについて、ゼロをチェックするのではなく、マジック値と等しいかどうかをチェックするようにします。これは同等性テストのセマンティクスに基づいていますが、コンパイラーは内部的に異なる方法で実装する場合があります。§6.5.13/ 3、§6.5.14/ 3、§6.5.15/ 4、§6.5.3.3/ 5、§6.8.4.1/ 2、§6.8.5/ 4を参照
- cafが指摘したように、静的オブジェクトの初期化(§6.7.8/ 10)および部分複合初期化子(§6.7.8/ 21)のセマンティクスを更新して、新しいnullポインター表現を反映させます。
- 真のアドレス0にアクセスする別の方法を作成します。
処理する必要のないものがいくつかあります。例えば:
int x = 0;
void *p = (void*)x;
この後p
、nullポインタであることが保証されません。定数の割り当てのみを処理する必要があります(これは、真のアドレス0にアクセスするための優れたアプローチです)。同様に:
int x = 0;
assert(x == (void*)0); // CAN BE FALSE
また:
void *p = NULL;
int x = (int)p;
x
であるとは限りません0
。
要するに、この状態はC言語委員会によって明らかに検討され、NULLの代替表現を選択する人のために考慮が払われました。あなたが今しなければならないすべてはあなたのコンパイラーに大きな変更を加えることです、そしてちょっとちょっとあなたはやっつけられました:)
補足として、コンパイラーが適切になる前に、ソースコード変換ステージでこれらの変更を実装できる場合があります。つまり、プリプロセッサ->コンパイラ->アセンブラ->リンカの通常のフローの代わりに、プリプロセッサ-> NULL変換->コンパイラ->アセンブラ->リンカを追加します。次に、次のような変換を行うことができます。
p = 0;
if (p) { ... }
/* becomes */
p = (void*)-1;
if ((void*)(p) != (void*)(-1)) { ... }
これには、完全なCパーサーと、タイプパーサー、typedefと変数宣言の分析が必要であり、どの識別子がポインターに対応するかを決定します。ただし、これにより、コンパイラのコード生成部分を適切に変更する必要がなくなります。clangはこれを実装するのに役立つかもしれません-私はそれがこのような変換を考慮して設計されたことを理解しています。もちろん、標準ライブラリにも変更を加える必要があります。
mprotect
。または、プラットフォームにASLRなどがない場合、プラットフォームの物理メモリを超えてアドレス指定します。幸運を。