gpt4 book ai didi

c++ - 使用 memcpy 和 memset 重新分配数组

转载 作者:行者123 更新时间:2023-12-01 18:58:03 25 4
gpt4 key购买 nike

我接管了一些代码,并遇到了一个奇怪的数组重新分配。这是 Array 类中的函数(由 JsonValue 使用)

void reserve( uint32_t newCapacity ) {
if ( newCapacity > length + additionalCapacity ) {
newCapacity = std::min( newCapacity, length + std::numeric_limits<decltype( additionalCapacity )>::max() );
JsonValue *newPtr = new JsonValue[newCapacity];

if ( length > 0 ) {
memcpy( newPtr, values, length * sizeof( JsonValue ) );
memset( values, 0, length * sizeof( JsonValue ) );
}

delete[] values;

values = newPtr;
additionalCapacity = uint16_t( newCapacity - length );
}
}

我明白了这一点;它只是分配一个新数组,并将旧数组中的内存内容复制到新数组中,然后将旧数组的内容清零。我也知道这样做是为了防止调用析构函数和移动。

JsonValue 是一个具有函数的类,以及一些存储在 union 中的数据(字符串、数组、数字等)。

我关心的是这是否是真正定义的行为。我知道它有效,而且自从我们几个月前开始使用它以来就没有出现过问题;但如果它未定义,则并不意味着它会继续工作。

编辑:JsonValue 看起来像这样:

struct JsonValue {
// …
~JsonValue() {
switch ( details.type ) {
case Type::Array:
case Type::Object:
array.destroy();
break;
case Type::String:
delete[] string.buffer;
break;
default: break;
}
}
private:
struct Details {
Key key = Key::Unknown;
Type type = Type::Null; // (0)
};

union {
Array array;
String string;
EmbedString embedString;
Number number;
Details details;
};
};

其中 ArrayJsonValue 数组的包装器,Stringchar*EmbedStringchar[14]Numberint 的并集,unsigned int ,和 doubleDetails 包含它所保存的值的类型。所有值在开头都有 16 位未使用的数据,用于详细信息。示例:

struct EmbedString {
uint16_t : 16;
char buffer[14] = { 0 };
};

最佳答案

此代码是否具有明确定义的行为基本上取决于两件事:1) 是 JsonValue trivially-copyable 2) 如果是这样,一堆全零字节是 JsonValue 的有效对象表示。

如果 JsonValue 是可简单复制的,那么从一个 JsonValue 数组到另一个数组的 memcpy 确实相当于复制所有元素超过 [basic.types]/3 。如果全零是 JsonValue 的有效对象表示,那么 memset 应该没问题(我相信这实际上属于当前的灰色区域)标准的措辞,但我相信至少其意图是这样就可以了)。

我不确定为什么您需要“防止调用析构函数和移动”,但用零覆盖对象并不会阻止析构函数运行。 delete[] value 调用数组成员的析构函数。无论如何,移动可简单复制类型的数组的元素应该编译为仅复制字节。

此外,我建议摆脱这些 StringEmbedString 类,而只使用 std::string。至少,在我看来,EmbedString 的唯一目的是手动执行小字符串优化。任何有值(value)的 std::string 实现都已经在幕后做到了这一点。请注意,std::string 不保证(并且通常不会)可简单复制。因此,您不能简单地用 std::string 替换 StringEmbedString,同时保留当前实现的其余部分。

如果你可以使用C++17,我建议简单地使用 std::variant而不是或至少在这个自定义 JsonValue 实现中,因为这似乎正是它想要做的。如果您需要在变体值之前存储一些通用信息,只需在保存变体值的成员前面放置一个合适的成员来保存该信息,而不是依赖于以相同的一对开始的 union 的每个成员成员(只有当所有 union 成员都是标准布局类型并将此信息保留在其公共(public)初始序列 [class.mem]/23 中时,这才是明确定义的)。

Array 的唯一目的似乎是作为一个 vector ,在出于安全原因释放内存之前将其清零。如果是这种情况,我建议只使用带有分配器的 std::vector ,该分配器在取消分配之前将内存归零。例如:

template <typename T>
struct ZeroingAllocator
{
using value_type = T;

T* allocate(std::size_t N)
{
return reinterpret_cast<T*>(new unsigned char[N * sizeof(T)]);
}

void deallocate(T* buffer, std::size_t N) noexcept
{
auto ptr = reinterpret_cast<volatile unsigned char*>(buffer);
std::fill(ptr, ptr + N, 0);
delete[] reinterpret_cast<unsigned char*>(buffer);
}
};

template <typename A, typename B>
bool operator ==(const ZeroingAllocator<A>&, const ZeroingAllocator<B>&) noexcept { return true; }

template <typename A, typename B>
bool operator !=(const ZeroingAllocator<A>&, const ZeroingAllocator<B>&) noexcept { return false; }

然后

using Array = std::vector<JsonValue, ZeroingAllocator<JsonValue>>;

注意:我通过 volatile unsigned char* 填充内存,以防止编译器优化归零。如果需要支持过度对齐类型,可以将 new[]delete[] 替换为直接调用 ::operator new::operator delete (这样做将阻止编译器优化分配)。在 C++17 之前,您必须分配足够大的缓冲区,然后手动对齐指针,例如使用 std::align ...

关于c++ - 使用 memcpy 和 memset 重新分配数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59155283/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com