将 std::unique_ptr<uint8_t[]> 转换为结构unique_ptr而不丢失 uint8_t[] 缓冲区

Casting std::unique_ptr<uint8_t[]> to struct unique_ptr without losing uint8_t[] buffer

提问人:Maga 提问时间:10/27/2023 最后编辑:Mr FoozMaga 更新时间:10/27/2023 访问量:97

问:

有没有办法将 to 转换为,以便结构指向与原来的地址相同的地址并保存该缓冲区?std::unique_ptr<uint8_t[]>(n)std::unique_ptr</*some_struct*/>uint8_t ptr

auto buffer = std::make_unique<uint8_t[]>(100);
std::unique_ptr<dummy> header = std::move(buffer); // error
struct dummy // string data
{
    uint64_t unk1; // 0x0

    uint64_t string_offset; // 0x8

    uint32_t c59d1c81; // 0x10

    uint16_t byte_length; // 0x14
    uint16_t string_length; // 0x16

    uint64_t unk2; // 0x18

    uint8_t* get_string() { return (uint8_t*)(uint64_t(this) + 0x8 + string_offset); }
};

在此示例中,buffer 保存结构的字节。但是,它还包含一个字符串,该字符串可通过成员和方法获取。0x20dummystring_offsetget_string

C++ 铸造 unique-ptr

评论

1赞 paddy 10/27/2023
这听起来像是在问新的安置,尽管有点不清楚你想实现什么。我认为在这种情况下,无法保证您的缓冲区具有正确的对齐方式。
0赞 ShadowRanger 10/27/2023
可以肯定的是,即使对齐问题不是一回事,也无法按照您尝试的方式安全地完成。 存储与 完全不同的元数据,并且不会知道必须根据普通数组的规则释放托管内存。有一些方法可以在其中创建一个对象,但它仍然会违反分配规则 AFAICT。你能解释一下你为什么要这样做吗?这有点像 XY 问题new[]newstd::unique_ptrstructuint8_t
0赞 Sam Varshavchik 10/27/2023
根据定义,搁置强制转换问题意味着没有指向该对象的其他指针。任何类型。这就是使它成为.否则,它就不会是 .unique_ptrunique_ptrunique_ptrunique_ptr
0赞 user4581301 10/27/2023
你可以尝试各种疯狂的蓝精灵来投射和 s,但它们都会以一种或另一种方式回到严格的锯齿违规。您可以安全地转换为字节并再次转换回来,但是将字节数组转换为另一个对象需要额外的保证,您必须自己管理。union
0赞 Maga 10/27/2023
我正在对游戏进行数据挖掘。读取数据块(在主题中是它的缓冲区)后,我必须将其转换为某个结构,同时将所有内容保留为指针。我已经使用带有 new/new[] 的原始指针实现了我的目标,并且刚刚尝试使用 std::unique_ptr 来实现。

答:

3赞 Jarod42 10/27/2023 #1

忽略可能的锯齿/对齐问题,您可能会这样做

auto buffer = std::make_unique<uint8_t[]>(100);
std::unique_ptr<dummy, Deleter> header(reinterpret_cast<dummy*>(buffer.release()));

struct Deleter
{
    void operator()(dummy* p) const {
        p->~dummy(); // Noop in your case
        delete[] reinterpret_cast<uint8*>(p);
    }
};

评论

0赞 zzz 10/27/2023
与 Deleter unique_ptr的绝佳解决方案。但是对于基元类型 (POD),删除 uint* 与删除 void* 相同,与删除 dummy* 相同,不是吗?我相信这是在实践中,但不确定是否有任何违反标准的行为。
0赞 Jarod42 10/27/2023
@zzz:删除是UB(就像“不完整”类型一样)。删除 uint* 与删除 dummy* 不同(的类型应与 的类型匹配)(即使在实践中它可能会起作用)。Violation 用于严格的别名和生存期(虚拟生存期当前从不开始),但与 无关。void*voiddeletenewstd::unique_ptr
0赞 Maga 10/27/2023
@PatrickRoberts 出色的自定义删除器解决方案,谢谢!我将研究 delete[] 运算符如何与 uint8_t* 强制转换配合使用,但至少它现在有效。顺便说一句,它应该在标头初始化中是reinterpret_cast<dummy*>。
0赞 Maga 10/27/2023
@Jarod42 对不起,给你贴错了标签。再次感谢!
0赞 zzz 10/30/2023
@Jarod42 真的。我的意思是“任何原始类型”而不是“void”。