C++ 64位系统上指针值的范围

C++ 64位系统上指针值的范围,c++,c,pointers,C++,C,Pointers,最近我在读关于小字符串优化(SSO)的文章:。我们知道,字符串通常由3个指针组成,在64位系统中为24字节。链接的答案是,在libc++的实现中,第一个指针的第一位用于指示字符串是处于“长”还是“短”模式,即堆分配和外部存储与内部存储(最多22个字符) 然而,这假设第一个指针的第一位永远不能有意义地成为地址的一部分,因为每当字符串处于“长”模式时,该位将始终被设置(或取消设置,取决于选择的约定)。从表面上看,这似乎是合理的,因为64位指针允许2^64个地址,大于1,后跟18个字节的零,或者超过1

最近我在读关于小字符串优化(SSO)的文章:。我们知道,字符串通常由3个指针组成,在64位系统中为24字节。链接的答案是,在libc++的实现中,第一个指针的第一位用于指示字符串是处于“长”还是“短”模式,即堆分配和外部存储与内部存储(最多22个字符)

然而,这假设第一个指针的第一位永远不能有意义地成为地址的一部分,因为每当字符串处于“长”模式时,该位将始终被设置(或取消设置,取决于选择的约定)。从表面上看,这似乎是合理的,因为64位指针允许2^64个地址,大于1,后跟18个字节的零,或者超过10亿GB

所以这是合理的,尽管不确定。我的问题是:这在什么地方有保证吗?如果有保证,哪里有保证?根据架构规范,还是其他什么?更进一步地说:用多少比特做这件事是安全的?我模糊地记得在某个地方读到的只有48位被使用,但我不记得了

如果有一定数量的比特,例如8或16保证不被触及,那么这肯定是可以以一些有趣的方式利用的。利用这一点会很好,但不会以代码在某些机器上神秘失败为代价。

长位不是指针的一部分,而是容量的一部分:

struct __long
{
    size_type __cap_;
    size_type __size_;
    pointer   __data_;
};
“诀窍”是,如果您始终分配偶数个字符,并为nul终止符保留一个字符,则生成的容量将始终是奇数。你可以免费得到1比特

我们知道,字符串通常由3个指针组成,在64位系统中为24字节

对于libc++,情况并非如此。“长字符串”的
\u long
结构定义为:

struct __long
{
    size_type __cap_;
    size_type __size_;
    pointer   __data_;
};
因此,短标志进入容量字段,使整个事件变得毫无意义

至于指针标记,没有关于指针大小的通用保证。在x86_64上,CPU用于虚拟地址转换的数据结构仅使用48位(或具有物理地址扩展的52位),因此虚拟地址从不使用较高的16位(或12位)。此外,大多数操作系统将其内核映射到每个进程中,并为其保留一定数量的高端地址空间,所以在实践中,用户模式指针甚至受到更大的限制。在Windows上,指针最重要的硬件可用位表示它属于内核空间还是用户空间


这些限制将来可能会发生变化,并且会随平台而变化,因此在独立于平台的标准库中使用它们将是一种不好的形式。一般来说,最好的做法是这样做,因为您的应用程序可以控制它们。

安腾和Alpha是非常不同的平台。你所能做的最好的事情就是对操作系统使用的内存布局发表声明。@Flexo好吧,这已经是一个部分答案了,如果安腾规范对此有什么要说的,那么这就是一个开始。已经投票结束了吗?我能问一下为什么吗?它更有可能是最不重要的,而不是最重要的。长字符串缓冲区将从堆中分配,并且几乎肯定是8byte对齐的(至少),因此底部四位为零。这避免了对整个内存布局做出任何假设。你在很多方面都错了。短字符串标志与指针完全不重叠,它与保存长字符串大小的字段重叠。即使它确实与指针重叠,它也将是指针的低位,这意味着内存范围不会进入它,只会对齐。实际上,x86-64具有规范地址,因此上面的16位是位47的副本,因此可以设置它们。@EOF这能保证吗?如果是这样的话,那么听起来实际上可以将这些位用于某些事情,只需在解引用之前设置前16位即可。@Nirfiedman:是的,规范指针是由硬件强制执行的,如果尝试解引用非规范地址,则会出现异常。但是,如果虚拟地址空间在以后的处理器代中扩展,依赖于这些未使用位的代码将不再工作。我很确定规范地址应该不鼓励这种使用。感谢你指出这一点,我愚蠢地认为字符串使用了3个指针,就像向量通常使用的那样,肯定是大脑放屁。我接受了zneak的回答,因为他补充了更多关于我问题核心的信息,但也要感谢你!