如何在c/c++中检查两个任意内存范围是否重叠
How to check that two arbitrary memory ranges are not overlapped in c/c++
假设我需要实现一个非常通用的实用程序函数,它必须将一个缓冲区复制到另一个缓冲区时。根据c/c++标准,使用具有重叠内存的memcpy是UB,另一方面memmove可能较慢。我希望能够在任何可能的时候使用memcmp实现,并在必要时切换到memmove
void copy(const void* src, int src_size, void* dst, int dst_size, int count)
{
assert(src);
asserc(dst);
assert(0 < count);
assert(count < src_size);
assert(count < dst_size);
if (are_overlaped(src, dst, count))
std::memove(dst, src, count);
else
std::memcmp(dst, src, count);
}
根据c/c++标准,与任意指针进行比较或相减是UB。为了编写一个简单的are_overlaped实现,两个指针都必须指向同一数组的项。所以这是UB在一般情况下
bool are_overlaped(const void* first, const void* second, int size)
{
assert(first);
asserc(second);
assert(0 < size);
return std::abs(reinterpret_cast<const char*>(first)
- reinterpret_cast<const char*>(second)) < size;
}
所以我的问题是如何正确实现任意指针的are_overlaped?或者有标准的检查方法吗?
PS。带副本的示例只是一个更好理解的上下文。我想知道如何实现are_overlaped,而不是使用或实现复制。
指针上的比较是UB,如果这两个指针不属于同一个数组,则可以使用std::less
,类似于:
template <typename T>
bool are_overlaped(const T* first, const T* second, int size)
{
// `first` and `second` should be arrays of size greater or equal to `size`
return first && second
&& (!std::less<>{}(second, first) && std::less<>{}(second, first + size)
|| !std::less<>{}(first, second) && std::less<>{}(first, second + size));
}
这是合法的:
bool inside( void const* a, void const* b, size_t count ){
if (count==0) return false;
if (count==1) return ptr_equal(a,b);
return inside(a, b, count/2)||inside(a,static_cast<char const*>(b)+count/2, count-count/2 );
}
其中CCD_ 2执行CCD_ 3或CCD_;我认为第二个定义很好,但第一个是等价的,也会很好地定义。
下一个
bool overlap(void const* lhs, size_t lhs_size, void const* rhs, size_t rhs_size ){
if (!lhs_size || !rhs_size) return false;
auto a0 = static_cast<char const*>(lhs);
auto b0 = static_cast<char const*>(rhs);
return inside(a0, b0, rhs_size) || inside(a0+lhs_size-1, b0, rhs_size) || inside(b0, a0, lhs_size) || inside(b0+rhs_size-1, a0, lhs_size);
}
祈祷你的优化器能在理智的平台上完成它的工作。好吧,检查一下。
我对这两个答案都想了很多。尽管@Yakk-Adam Nevraumont是完全正确的,但他的解决方案不太可能有用。另一方面,@Jarod42的答案是可用的,但不是100%正确。我们能把这两种解决方案混合在一起,以获得足够的正确性和合理的价格吗?当然,我认为这是有限制的。
bool may_be_overlapped(void const* lhs, size_t lhs_size_in_bytes, void const* rhs, size_t rhs_size_in_bytes)
{
if (lhs_size_in_bytes == 0 || rhs_size_in_bytes == 0)
return false;
assert(lhs != nullptr);
assert(rhs != nullptr);
if (std::less<>(reiterpret_cast<const char*>(lhs) + lhs_size_in_bytes - 1, rhs)
|| std::less<>(reiterpret_cast<const char*>(rhs) + rhs_size_in_bytes - 1, lhs))
return false;
return true;
}
// wide contract
[[nodiscard]]
bool safe_copy(void* dst, size_t dst_size_in_bytes, const void* src, size_t src_size_in_bytes, size_t count_in_bytes)
{
if (count_in_bytes == 0)
return true;
if (dst == nullptr || src == nullptr
|| dst_size_in_bytes < count_in_bytes
|| src_size_in_bytes < count_in_bytes)
{
return false;
}
std::memmove(dst, src, count_in_bytes);
return true;
}
// narrow contract
void not_overlaped_copy(void* dst, size_t dst_size_in_bytes, const void* src, size_t src_size_in_bytes, size_t count_in_bytes)
{
if (count_in_bytes == 0)
return;
assert(dst != nullptr);
assert(src != nullptr);
assert(dst_size_in_bytes >= count_in_bytes);
assert(src_size_in_bytes >= count_in_bytes);
assert(!may_be_overlapped(dst, count_in_bytes, src, count_in_bytes));
std::memcpy(dst, src, count_in_bytes);
}
// narrow contract
void copy(void* dst, size_t dst_size_in_bytes, const void* src, size_t src_size_in_bytes, size_t count_in_bytes)
{
if (count_in_bytes == 0)
return;
assert(dst != nullptr);
assert(src != nullptr);
assert(dst_size_in_bytes >= count_in_bytes);
assert(src_size_in_bytes >= count_in_bytes);
std::memmove(dst, src, count_in_bytes);
}
PS。如果您确实需要are_overlapped,它可以在不考虑内存复制上下文的情况下使用,那么下面是Yakk解决方案的set_intersection版本:
bool are_overlapped(void const* lhs, size_t lhs_size_in_bytes, void const* rhs, size_t rhs_size_in_bytes)
{
if (lhs_size_in_bytes == 0 || rhs_size_in_bytes == 0)
return false;
assert(lhs != nullptr);
assert(rhs != nullptr);
auto first1 = reiterpret_cast<const char*>(lhs);
const auto last1 = first1 + lhs_size_in_bytes - 1;
auto first2 = reiterpret_cast<const char*>(rhs);
const auto last2 = first2 + rhs_size_in_bytes - 1;
if (std::less<>(last1, first2) || std::less<>(last2, first1))
return false;
while (first1 <= last1 && first2 <= last2)
{
if (std::less<>(first1, first2))
++first1;
else if (std::less<>(first2, first1))
++first2;
else
retun true;
}
return false;
}
不要那样做
memmove和memcpy一样高效,只是检查了一次重叠。
相关文章:
- 如何在C++中从两个单独的for循环中添加两个数组
- 为什么两个不同的未命名名称空间可以共存于一个cpp文件中
- 当在同一名称空间中有两个具有相同签名的函数时,会发生什么
- 如何返回一个类的两个对象相加的结果
- 如何在C++中将一个无符号的 int 转换为两个无符号的短裤?
- 如何将两个不同矢量的同一位置的两个元素组合在一起
- 两个字符串在 c++ 中不相等
- 在两个类中共享相同的函数调用,并在不需要时避免空实例化
- 两个文件使用彼此的功能-如何解决
- 为什么Mat类的两个对象可以在不重载运算符+的情况下添加
- 如何确保在使用基于布尔值的两个方法之一调用方法时避免分支预测错误
- 停止cmake target_link_libraries将插件中静态库的两个对象文件链接到静态库本身
- 将fold表达式与std::一起用于两个元组
- 如何在C++中比较两个char数组
- 如何在c/c++中检查两个任意内存范围是否重叠
- 是否可以将两个任意函数与 C++17 中的 std::any 进行比较
- 一个数组可以划分的最大子数组,使得不同子数组中任意两个元素的 GCD 始终为 1?
- C++ 数组任意两个项相等
- 为什么我的程序是,数组中任意两个元素之间的最小差异,给出相反的结果
- 两个成员中任意一个成员的哈希