繁体   English   中英

在字节数组中保存一个空指针

[英]Saving a void pointer in an array of bytes

我有这个家庭作业,其中包括我对字符串 class 实施缓冲区优化。我必须保存从字节 8 到 16 的指针,因为第一个字节用于查看字符串是堆分配的还是堆栈分配的allocated,1-7字节用来保存字符串的长度。 我需要帮助找出如何保存指向动态分配的 memory 的指针,然后从字节 8-16 返回它。

我如何创建缓冲区:

alignas(CharT) std::array<std::byte, SZ> buffer;

我想如何返回缓冲区:

return reinterpret_cast<CharT *>(buffer[8]);

我尝试使用memcpy()memmove() ,但是当我这样做时,我只能从原始字符串返回 8 个字节。

我如何创建和操作指针:

auto ptr = ::operator new[](count_bytes, std::align_val_t{std::alignment_of_v<CharT>});
std::memcpy(ptr, sv.data(), count_bytes);
std::memmove(&buffer[8], ptr, sizeof(ptr));

缓冲区的实现:

template<typename CharT = char, std::size_t SZ = 32>
struct storage {
private: 
    alignas(CharT) std::array<std::byte, SZ> buffer;

public:

    void set(const std::basic_string_view<CharT> &sv) { 
        std::size_t count_bytes = sizeof(CharT) * (sv.length() + 1); ///counts the bytes of the string
        auto ptr = ::operator new[](count_bytes, std::align_val_t{std::alignment_of_v<CharT>}); /// make a pointer to the memory location
        std::memcpy(ptr, sv.data(), count_bytes);
        std::memmove(&buffer[8], &ptr, sizeof(ptr));
        operator delete[](ptr);
    }

    const CharT *str() {
        /// Returs a pointer to the first byte of the string
        return reinterpret_cast<CharT *>(buffer[8]);
    }

我发现您的代码有几个问题:

  • 不需要直接使用::operator new[] ,通常使用new CharT[]就足够了。

  • 您正在将 1 太多CharTsv复制到您正在分配的 memory 中。

  • buffer[8]是一个byte ,您将其重新解释为指针,这是错误的。 您需要重新解释该字节的地址 如果您打算将CharT*指针返回到您的长度和标志字节之后的实际字节(即,对于堆栈分配的字符串),那么将字节地址重新解释为CharT*就可以了。 但是,如果您打算返回存储的CharT*指针(即返回堆分配的字符串),那么您需要将字节地址重新解释为CharT**并取消引用该指针。

  • 您没有将分配的 memory 长度或分配标志存储到buffer中,正如您所描述的那样,它必须包含。

  • ptr的值复制到您的buffer后,您正在调用operator delete[]来释放您刚刚分配的 memory,从而使buffer留下悬空指针。

  • 如果buffer持有动态分配 memory 的指针,并且再次调用set() ,则先前分配的 memory 被泄漏。

话虽如此,请尝试更像这样的事情:

template<typename CharT = char, std::size_t SZ = 32>
struct storage {
private: 
    static_assert(SZ >= (8 + sizeof(CharT*))), "SZ is too small");

    struct header {
        std::uint64_t flag: 8;
        std::uint64_t length: 56;
        union {
            CharT* ptr;
            std::byte data[SZ-8];
        } u;
    };

    alignas(CharT) std::array<std::byte, SZ> buffer;

public:

    void clear() {
        header *hdr = reinterpret_cast<header *>(&buffer[0]);
        if (hdr->flag == 1)
            delete[] hdr->u.ptr;
        buffer.fill(0);
    }

    void set(const std::basic_string_view<CharT> &sv) { 
        std::uint64_t len = sv.length();
        if (len > 0x00FFFFFFFFFFFFFFULL) { /// make sure the length fits in 7 bytes
            throw std::length_error("sv is too long in length");
        }

        clear();

        header hdr;
        hdr.flag = 1;
        hdr.length = len;
        hdr.u.ptr = new CharT[len+1]; /// make a pointer to the memory location
        std::copy_n(sv.data(), len, hdr.u.ptr);
        hdr.u.ptr[len] = static_cast<CharT>(0);

        std::memcpy(&buffer, &hdr, sizeof(hdr));
    }

    const CharT* str() const {
        /// Returns a pointer to the first byte of the string
        const header *hdr = reinterpret_cast<const header *>(&buffer[0]);
        if (hdr->flag == 1)
            return hdr->u.ptr;
        else
            return reinterpret_cast<const CharT*>(hdr->u.data);
    }

    uint64_t length() const {
        /// Returns the string length
        return reinterpret_cast<const header *>(&buffer[0])->length;
    }

    ...
};

话虽这么说,我会通过使flag字段为 1 位而不是 1 个完整字节来更进一步,从而为length字段提供 7 位以供使用,例如:

template<typename CharT = char, std::size_t SZ = 32>
struct storage {
private: 
    ...

    struct header {
        std::uint64_t flag: 1;
        std::uint64_t length: 63;
        ...
    };

    ...

public:

    void set(const std::basic_string_view<CharT> &sv) { 
        std::uint64_t len = sv.length();
        if (len > std::numeric_limits<int64_t>::max()) { /// make sure the length fits in 63 bits
            throw std::length_error("sv is too long in length");
        }

        ...
    }

    ...
};

字节 1-7 用于保存长度

你的意思是字节 0-7。

std::memmove(&buffer[8], ptr, sizeof(ptr));

memmove的第二个参数是要复制的字节的起始地址。 您告诉memmoveptr指向的 memory 地址复制sizeof(ptr)字节。

您描述您的预期目标是复制指针本身,它的sizeof(ptr)字节,而不是从指针指向的任何地方复制 sizeof(ptr sizeof(ptr) 如果是这样,那么这应该是:

std::memmove(&buffer[8], &ptr, sizeof(ptr));

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM