[英]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 太多CharT
从sv
复制到您正在分配的 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
的第二个参数是要复制的字节的起始地址。 您告诉memmove
从ptr
指向的 memory 地址复制sizeof(ptr)
字节。
您描述您的预期目标是复制指针本身,它的sizeof(ptr)
字节,而不是从指针指向的任何地方复制 sizeof(ptr sizeof(ptr)
。 如果是这样,那么这应该是:
std::memmove(&buffer[8], &ptr, sizeof(ptr));
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.