std::midpoint计算指针中点安全但仅限同数组(含one-past-the-end),需c++20、、同类型原生指针;不支持迭代器、跨数组、nullptr或悬空指针,且不校验有效性。

std::midpoint 计算指针中点是安全的,但仅当两个指针指向同一数组(或 one-past-the-end)时才合法;否则行为未定义,编译器不会报错,运行时可能崩溃或返回错误地址。
std::midpoint(ptr_a, ptr_b) 要求同数组、同类型、可相减
它底层依赖 ptr_b - ptr_a 得到 ptrdiff_t 偏移,再做除法,最后加回 ptr_a。这意味着:
- 必须包含
<numeric></numeric>,且启用 C++20(-std=c++20) -
ptr_a和ptr_b类型必须完全一致,比如都是int*,不能是const int*与int*混用(除非有隐式转换) - 两者必须指向同一数组,或其中一个是
&arr[N](one-past-the-end);跨数组、悬空指针、nullptr都是未定义行为 - 不支持
std::vector::iterator等类指针类型——即使它满足 RandomAccessIterator,std::midpoint也不接受,除非你显式转成原生指针:std::midpoint(&v[0], &v[0] + v.size())
为什么不能直接用 (ptr_a + ptr_b) / 2
这个表达式在 C++ 中根本无法编译:
-
ptr_a + ptr_b是非法操作——指针只允许加整数,不允许加另一个指针 - 即使你写成
static_cast<char>(ptr_a) + static_cast<char>(ptr_b)</char></char>,结果仍是char*,除以 2 后类型不匹配,语义混乱 -
std::midpoint的设计目标就是替代手写“安全中点”,它把指针当作带单位的偏移量处理,而不是裸地址数字
常见误用:传入迭代器或容器边界
以下写法看似合理,但多数会编译失败或行为未定义:
-
std::midpoint(v.begin(), v.end())→v.begin()是std::vector<t>::iterator</t>,非原生指针,不满足要求 -
std::midpoint(data(arr), data(arr) + size(arr))→ 正确,但前提是arr是 C 风格数组或支持std::data的容器 -
std::midpoint(p, p + N)→ 安全,只要p有效且N >= 0 -
std::midpoint(p, q)其中q → 允许,结果仍为 <code>⌊(p+q)/2⌋地址,但需确保q可从p到达(即p - q非负且可表示)
实际二分查找中怎么用才不出错
在基于指针的二分里,别只盯着“中点计算”本身,更要检查上下文合法性:
- 起始指针不能为
nullptr,结束指针必须 ≥ 起始指针(或为 one-past-the-end) - 不要对空范围调用:
if (first == last) return not_found;应在std::midpoint前判断 - 示例安全写法:
int arr[] = {1, 3, 5, 7, 9}; int* first = arr; int* last = arr + 5; // one-past-the-end while (first target) { last = mid; } else { return mid; } } - 最容易被忽略的是:std::midpoint 不校验指针有效性——它假设你传进来的是合法的。一旦传了野指针,崩溃发生在计算之后,调试时很难关联到这行代码
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











