将指针转换为指针…再转换为指针?

4
我找到了这个片段。
void* operator new(size_t nbytes)
{
  if (nbytes == 0)
    nbytes = 1;                    // so all alloc's get a distinct address
  void* ans = malloc(nbytes + 4);  // overallocate by 4 bytes
  *(Pool**)ans = NULL;             // use NULL in the global new
  return (char*)ans + 4;           // don't let users see the Pool*
}

这里是https://isocpp.org/wiki/faq/dtors

我花了一个多小时试图理解*(Pool**)ans = NULL;的含义。 ans是一个void指针,所以我会认为它被转换为Pool指针并将池设置为0。不是指针而是池本身,因为左边有第三个*。但是,Pool没有定义operator=

在声明中的pointer**显然是指向指针的指针......但在这种情况下,对我来说这毫无意义,因为ans是单个指针。

2个回答

7

在此处使用 Pool** 的唯一原因是语义正确,因为假设那个“隐藏”的 4 字节头应该是一个指向 Pool 的指针(因此 ans 是指向指向 Pool 的指针的指针,而 *(Pool **)ans 具有类型 Pool *)。

除非您能将 NULL 分配给 Pool,否则无法执行 *(Pool *)ans = NULL,而且这可能也不是预期的效果。像 *(int **)ans = NULL 或更荒谬的 *(Pool ******)ans = NULL 会产生相同的最终效果,但如果最终意图是指向 Pool 的指针,则在语义上会有些奇怪。

最终你会得到:

 +---+---+---+---+- - -
 | 0 | 0 | 0 | 0 | ... and nbytes more bytes
 +---+---+---+---+- - -

 ^ ans           ^ returned address (ans + 4)

这里的前4个字节是指向某个Pool的指针。

换个思路,忽略整个nbytes的概念,可以考虑这种通用模式:

void * ptr = malloc(sizeof(TYPE));
*(TYPE *)ptr = VALUE;

这应该很容易理解。如果TYPE是Pool *,VALUE是NULL,并且你将其套入该模式中,你可以看到它仍然很有意义:

void * ptr = malloc(sizeof(Pool *));
*(Pool **)ptr = NULL;

针对你的情况,你仍然基本上在这样做,尽管你在末尾分配了一些额外的字节,但这与这里的类型无关。

顺便说一下,在这里硬编码 4 而不是 sizeof(Pool *) 可能会引发麻烦(而且语法上懒惰)。


3
不,它并不是“转换为池指针”。这个转换操作是一个:
(Pool**)

这不是指向 Pool 的指针。这是指向指向 Pool 的指针。

因此,现在假设 ans 是一个 Pool **,因为它就是这样的。在这种情况下:

*ans = NULL;

现在,这会将指向ans的指针设置为NULL,而不是Pool类的某个实例。但是它指向了一个指针。

但是这里有一个更大的问题:

 void* ans = malloc(nbytes + 4);  // overallocate by 4 bytes
 *(Pool**)ans = NULL;             // use NULL in the global new
 return (char*)ans + 4;           // don't let users see the Pool*

这是非常老的代码,只有指针长度为4字节时才能正常工作。

在现代64位平台上,由于指针长度为8字节,整个程序将彻底失败...


这不是“奇怪”的。这是一种假设所有指向对象的指针都是4字节(通常为32位)和相关对齐的代码。4可能最好重写为sizeof(Pool *)。或者完全删除它 - 偏移量更像是一种肮脏的技巧(程序员试图添加伪安全措施),并且在实际使用中没有太大作用。(如果删除它,请同时修改其他直接操作Pool的函数,因为它们也将使用该偏移量)。 - Peter
@Peter Sam 输入的是"OLD"而不是"ODD"。 - Jason C

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接