重新定义 NULL [英] Redefining NULL

查看:28
本文介绍了重新定义 NULL的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为地址 0x0000 有效且包含端口 I/O 的系统编写 C 代码.因此,访问 NULL 指针的任何可能的错误都不会被发现,同时会导致危险的行为.

I'm writing C code for a system where address 0x0000 is valid and contains port I/O. Therefore, any possible bugs that access a NULL pointer will remain undetected and at the same time cause dangerous behaviour.

出于这个原因,我希望将 NULL 重新定义为另一个地址,例如一个无效的地址.如果我不小心访问了这样的地址,我将收到一个硬件中断,我可以在其中处理错误.我碰巧可以访问此编译器的 stddef.h,因此我实际上可以更改标准头文件并重新定义 NULL.

For this reason I wish to redefine NULL to be another address, to for example an address that isn't valid. If I accidentally access such an address I will get a hardware interrupt where I can handle the error. I happen to have access to stddef.h for this compiler, so I can actually alter the standard header and redefine NULL.

我的问题是:这会与 C 标准冲突吗?据我从标准中的 7.17 中可以看出,宏是实现定义的.标准中是否有其他内容说明 NULL must 为 0?

My question is: will this conflict with the C standard? As far as I can tell from 7.17 in the standard, the macro is implementation-defined. Is there anything elsewhere in the standard stating that NULL must be 0?

另一个问题是,无论数据类型如何,许多编译器都通过将所有内容设置为零来执行静态初始化.即使标准说编译器应该将整数设置为零并将指针设置为 NULL.如果我为我的编译器重新定义 NULL,那么我知道这样的静态初始化将失败.即使我大胆地手动更改了编译器标头,我是否可以将其视为不正确的编译器行为?因为我确信这个特定的编译器在进行静态初始化时不会访问 NULL 宏.

Another issue is that plenty of compilers perform static initialization by setting everything to zero, no matter the data type. Even though the standard says that the compiler should set integers to zero and pointers to NULL. If I would redefine NULL for my compiler, then I know that such static initialization will fail. Could I regard that as incorrect compiler behaviour even though I boldly altered compiler headers manually? Because I know for certain that this particular compiler does not access the NULL macro when doing static initialization.

推荐答案

C 标准不要求空指针指向机器的零地址.但是,将 0 常量转换为指针值必须导致 NULL 指针(第 6.3.2.3/3 节),并且将空指针评估为布尔值必须为 false.如果您确实确实想要一个零地址,而NULL 不是零地址,这可能会有点尴尬.

The C standard does not require null pointers to be at the machine's address zero. HOWEVER, casting a 0 constant to a pointer value must result in a NULL pointer (§6.3.2.3/3), and evaluating the null pointer as a boolean must be false. This can be a bit awkward if you really do want a zero address, and NULL is not the zero address.

尽管如此,通过对编译器和标准库的(大量)修改,让 NULL 用替代位模式表示,同时仍然严格遵守标准库并非不可能.然而,仅仅改变 NULL 本身的定义 是不够的,因为这样 NULL 将评估为真.

Nevertheless, with (heavy) modifications to the compiler and standard library, it's not impossible to have NULL be represented with an alternate bit pattern while still remaining strictly conformant to the standard library. It is not sufficient to simply change the definition of NULL itself however, as then NULL would evaluate to 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 节)以反映新的空指针表示.
  • 创建另一种访问真实地址零的方法.
  • Arrange for literal zeros in assignments to pointers (or casts to pointers) to be converted into some other magic value such as -1.
  • Arrange for equality tests between pointers and a constant integer 0 to check for the magic value instead (§6.5.9/6)
  • Arrange for all contexts in which a pointer type is evaluated as a boolean to check for equality to the magic value instead of checking for zero. This follows from the equality testing semantics, but the compiler may implement it differently internally. See §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
  • As caf pointed out, update the semantics for initialization of static objects (§6.7.8/10) and partial compound initializers (§6.7.8/21) to reflect the new null pointer representation.
  • Create an alternate way to access true address zero.

有些事情你不必处理.例如:

There are some things you do not have to handle. For example:

int x = 0;
void *p = (void*)x;

此后,p 不能保证为空指针.只需要处理常量赋值(这是访问真正地址零的好方法).同样:

After this, p is NOT guaranteed to be a null pointer. Only constant assignments need be handled (this is a good approach for accessing true address zero). Likewise:

int x = 0;
assert(x == (void*)0); // CAN BE FALSE

还有:

void *p = NULL;
int x = (int)p;

x 不保证为 0.

简而言之,C 语言委员会显然已经考虑了这种情况,并考虑了为 NULL 选择替代表示的人.您现在要做的就是对编译器进行重大更改,嘿,您已经完成了:)

In short, this very condition was apparently considered by the C language committee, and considerations made for those who would choose an alternate representation for NULL. All you have to do now is make major changes to your compiler, and hey presto you're done :)

作为旁注,可以在编译器正确之前通过源代码转换阶段来实现这些更改.也就是说,不是预处理器 -> 编译器 -> 汇编器 -> 链接器的正常流程,而是添加一个预处理器 -> NULL 转换 -> 编译器 -> 汇编器 -> 链接器.然后你可以做这样的转换:

As a side note, it may be possible to implement these changes with a source code transformation stage before the compiler proper. That is, instead of the normal flow of preprocessor -> compiler -> assembler -> linker, you'd add a preprocessor -> NULL transformation -> compiler -> assembler -> linker. Then you could do transformations like:

p = 0;
if (p) { ... }
/* becomes */
p = (void*)-1;
if ((void*)(p) != (void*)(-1)) { ... }

这将需要一个完整的 C 解析器,以及一个类型解析器和对 typedef 和变量声明的分析,以确定哪些标识符对应于指针.但是,通过这样做,您可以避免对编译器的代码生成部分进行适当的更改.clang 可能对实现这一点很有用 - 我知道它的设计考虑了这样的转换.当然,您仍然可能需要对标准库进行更改.

This would require a full C parser, as well as a type parser and analysis of typedefs and variable declarations to determine which identifiers correspond to pointers. However, by doing this you could avoid having to make changes to the code generation portions of the compiler proper. clang may be useful for implementing this - I understand it was designed with transformations like this in mind. You would still likely need to make changes to the standard library as well of course.

这篇关于重新定义 NULL的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆