非NULL保留指针值

时间:2013-12-21 11:14:33

标签: c++ c pointers magic-numbers type-punning

如何创建保留指针值?

上下文是这样的:我一直在考虑如何为动态脚本语言实现数据结构(我不打算实现它 - 只是想知道它将如何完成)。

字符串可以包含任意字节,包括NUL。因此,有必要单独存储该值。这需要一个指针(指向数组)和一个数字。第一个技巧是,如果指针为NULL,则它不可能是有效字符串,因此该数字可用于实际整数。

如果可以创建第二个保留指针值,则可以用来暗示其他字段现在被用作浮点值。可以这样做吗?

一种想法是mmap()没有权限的地址,也可以用来替换NULL指针的用法。

3 个答案:

答案 0 :(得分:6)

在任何现代系统中,您只需使用指针值12,... 4095即可。另一个常见的选择是(uintptr_t)-1,这在技术上较差,但使用频率高于1

为什么这些价值“安全”? 现代系统通过使得无法在虚拟地址零处映射任何内容来防止NULL指针访问。几乎所有空指针的解除引用都会触及这个不存在的区域,硬件会告诉操作系统发生了一些不好的事情,这会触发操作系统对该进程进行分段。
由于虚拟内存页面是页面对齐的(在当前硬件上至少为4k),并且没有任何内容映射到地址零,因此不能将任何内容映射到整个范围0, ..., 4095,以相同的方式保护所有这些地址,并且您可以将它们用作特殊目的值。

为此目的保留了多少虚拟内存空间是一个系统参数,在linux上由/proc/sys/vm/mmap_min_addr控制,root用户可以将其更改为零,这将禁用此保护(这不会是一个非常聪明的主意)。 Ubuntu的默认值是64k(即16页)。

这也是(uintptr_1)-1不如1安全的原因;即使任何超过一个字节的负载将达到零页面,地址(uintptr_1)-1本身也不一定以这种方式受到保护。因此,对(char*)-1执行字符串操作不一定是段错误。

修改
我对特殊映射的原始解释似乎有点陈旧,可能这是在旧的Mac / PPC平台上处理事情的方式。即使效果几乎相同,我也改变了答案的细节以反映现代linux。无论如何,重要的一点是如何实现空页保护,重要的是任何理智的现代系统都会有一些空页保护,至少包含提到的地址范围。在此SO答案中可以找到更多详细信息:https://stackoverflow.com/a/12645890/2445184

答案 1 :(得分:5)

在标准C(和标准C ++)中,100%有效且有效的方法很简单:声明一个变量,将其地址用作魔术值。

char *ptr;
char magic;
if (ptr == &magic) { ... }

这可以保证magic永远不会与另一个对象重叠。

诸如(char *) 1之类的魔术指针值也有它们的优点,但它很容易弄错(即使你忽略了(char *) 1可能是有效对象的理论实现,如果你使用{ {1}}作为魔术指针值,并且优化器假定(int *) 1值适当对齐,它可能会删除仅在100%有效代码中的无操作检查,而不是在我的代码中)标准方法,并且只有在您发现它们可以帮助您进行调试时,可选择暂时切换到魔术指针值。

答案 2 :(得分:1)

如果地址已经分配,​​

mmap地址可能会失败。可能最好使用某个静态变量或函数的地址。或者通过malloc(1)获取唯一地址。