C标准不需要将空指针指向机器的地址零。但是,将0
常量强制转换为指针值必须得到一个NULL
指针(第6.3.2.3/3节),并且将空指针评估为布尔值必须为false。这可以是一个有点尴尬,如果你真的不想要一个零个地址,而NULL
不是零个地址。
但是,通过对编译器和标准库进行(大量)修改,NULL
在仍然严格符合标准库的情况下,可以用备用位模式表示并非不可能。这是不足够简单地改变的定义NULL
然而本身,然后NULL
将评估为true。
具体来说,您需要:
- 安排指针分配(或强制转换为指针)中的文字零以转换为其他不可思议的值,例如
-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节)初始化的语义,以反映新的空指针表示形式。
- 创建访问真实地址零的替代方法。
有一些事情你不具备处理。例如:
int x = 0;
void *p = (void*)x;
此后,p
不能保证是空指针。只需要处理常量分配(这是访问真实地址零的好方法)。同样地:
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等,则地址超出平台物理内存。祝好运。