如何推进 void * 指针?

How to Advance void * pointer?

在 C++ 中我有:

MallocMetadata *tmp = static_cast<MallocMetadata *> (p);

但现在我希望 tmp 在内存中是 5 个字节,所以我尝试了:

MallocMetadata *tmp = static_cast<MallocMetadata *> (p-5);

但这并没有编译,我读了一些建议这样做的文章(但也没有用):

MallocMetadata *tmp = static_cast<MallocMetadata *> (static_cast<char *> (p) - 5);

如何解决这个问题,请注意:我确定内存中的位置是合法的,而且我希望 tmp 的类型为 MallocMetadata* 以便稍后使用。

您可以使用 reinterpret_castvoid* 以外的指针转换为其他指针。

MallocMetadata *tmp = reinterpret_cast<MallocMetadata *> (static_cast<char *> (p) - 5);

另一种选择是在减去某些内容后再次将 char* 转换为 void*

MallocMetadata *tmp = static_cast<MallocMetadata *> (static_cast<void *> (static_cast<char *> (p) - 5));

C++ How to Advance void * pointer?

无法推进 void*

将指针前进一个会修改指针以指向对象数组中先前指向的对象的下一个同级对象。数组的两个元素之间的距离因不同类型的对象而异。距离与物体大小完全一致

因此要推进一个指针,就必须要知道所指对象的大小。 void* 可以指向任何大小的对象,但无法从指针中获取有关该大小的信息。

您可以做的是将 void* 静态转换为指向对象的动态类型。只要类型是完整的,那么通过知道指针的类型就可以知道所指向对象的大小。然后,您可以使用指针算法将转换后的指针推进到所指向对象的同级对象。

But now I want tmp to be 5 bytes before in memory

在我们继续进行之前,我想明确指出,尝试这样做是不安全的,您必须详细了解语言规则,才能获得正确执行此操作的微乎其微的机会。我恳请您考虑是否有必要这样做。

要得到指向前5字节内存地址的指针,可以static_cast void*unsigned char*对转换后的指针做指针运算:

static_cast<unsigned char*>(p) - 5

MallocMetadata *tmp = static_cast<MallocMetadata *> (static_cast<char *> (p) - 5);

char* 不能静态转换为任意对象指针类型。 如果内存地址正确对齐((地址包含类似类型的对象)或(MallocMetadata是一个普通类型并且该地址不包含其他类型的对象,您将写入该地址而不是读取,从而创建一个新对象)),那么您可以使用 reinterpret_cast 代替:

MallocMetadata *tmp = reinterpret_cast<MallocMetadata*>(
    static_cast<char*>(p) - 5
);

完整示例:

// preparation
int offset = 5;
std::size_t padding = sizeof(MallocMetadata) >= offset
    ? 0
    : sizeof(MallocMetadata) - offset;
auto align = static_cast<std::align_val_t>(alignof(MallocMetadata));
void* p_storage = ::operator new(sizeof(MallocMetadata) + padding, align);
MallocMetadata* p_mm = new (p_storage) MallocMetadata{};
void* p = reinterpret_cast<char*>(p_mm) + offset;

// same as above
MallocMetadata *tmp = reinterpret_cast<MallocMetadata*>(
    static_cast<char*>(p) - offset
);

// cleanup
tmp->~MallocMetadata();
::operator delete(tmp);

我不知道你会怎么想,但我会试试:

标准中有一项要求,即 void * 和字符指针具有与 C 不同的相同表示和对齐方式,以及您过去使用的字符指针类型与现在使用 void * 的方式相同。

如果你有一个 void *,实际上是一个 void * 而不是其他类型的指针,并且你想一次将它前进一个字节,你应该能够创建一个引用-to-pointer-to-unsigned-character 绑定到 pointer-to-void,如:

auto &ucp = reinterpret_cast<unsigned char *&>(void_pointer);

现在应该可以通过对 ucp 引用的操作来操纵 void_pointer

所以 ++ucp 会提前,因此 void_pointer 会提前一个。