如何创建保留指针值?
上下文是这样的:我一直在考虑如何实现动态脚本语言的数据结构(我不打算实现这个,只是想知道如何实现)。
字符串可以包含任意字节,包括NUL。因此,有必要单独存储该值。这需要一个指针(指向数组)和一个数字。第一个技巧是,如果指针为NULL,则它不可能是有效的字符串,因此该数字可以用于实际的整数。
如果可以创建第二个保留指针值,这可以用来暗示另一个字段现在被用作浮点值。这能做到吗?
一种想法是mmap()一个没有权限的地址,这也可以用来代替NULL指针的使用。
在任何现代系统上,只需使用指针值1
、2
。。。CCD_ 3。另一个常见的选择是(uintptr_t)-1
,它在技术上较差,但使用频率高于1
。
为什么这些价值观是"安全的">
现代系统通过不可能映射虚拟地址0处的任何内容来防止NULL指针访问。几乎任何对NULL指针的取消引用都会碰到这个不存在的区域,硬件会告诉操作系统发生了不好的事情,这会触发操作系统中断进程
由于虚拟内存页面是页面对齐的(在当前硬件上至少4k),并且没有任何东西映射到地址零,因此没有任何东西可以映射到整个范围0, ..., 4095
,以相同的方式保护所有这些地址,并且您可以将它们用作特殊用途值。
为此保留多少虚拟内存空间是一个系统参数,在linux上由/proc/sys/vm/mmap_min_addr
控制,根用户可以将其更改为零,这将禁用这种保护(这不是一个非常明智的想法)。Ubuntu上的默认值是64k(即16页)。
这也是(uintptr_1)-1
的安全性不如1
的原因;即使超过一个字节的任何加载都将命中零页,地址(uintptr_1)-1
本身也不一定以这种方式受到保护。因此,在(char*)-1
上执行字符串操作不一定是segfault。
编辑:
我最初对特殊映射的解释似乎有点过时,可能这是旧Mac/PPC平台上处理事情的方式。尽管效果基本相同,但我更改了答案的细节以反映现代linux。无论如何,重要的一点不是如何实现空页保护,重要的是任何健全的现代系统都会有一些零页保护,至少包括上述地址范围。有关更多详细信息,请参阅SO的回答:https://stackoverflow.com/a/12645890/2445184
在标准C(和标准C++)中,100%有效且有效的方法很简单:声明一个变量,将其地址用作魔术值。
char *ptr;
char magic;
if (ptr == &magic) { ... }
这保证了magic
永远不会与另一个对象有任何重叠。
诸如CCD_ 13之类的魔术指针值也具有它们的优点,但很容易出错(即使你忽略(char *) 1
可能是有效对象的理论实现,如果你使用(int *) 1
作为魔术指针值,并且优化器假设int *
值适当对齐,它可能只会删除100%有效代码中没有操作的检查,而不是在你的代码中),我建议使用标准方法,只有当您发现魔术指针值有助于调试时,才可以选择临时切换到魔术指针值。
mmap
如果地址已分配,则寻址可能失败。可能最好使用某个静态变量或函数的地址。或者通过malloc(1)
获得唯一地址。