You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++中从对象指针计算缓冲区已放置对象偏移量的合法性与正确方法

问题分析与解答

核心结论:代码中(b)行存在未定义行为(UB)

你对标准规则的理解方向是正确的:根据C++标准[expr.add]p5的要求,两个指针相减的合法前提是——它们必须指向同一个数组的元素(或数组末尾后一位),且指针类型与数组元素类型匹配。

这里buf是std::byte数组衰减得到的std::byte*,指向数组的第0个元素;而bp是通过reinterpret_cast将S*转换而来的std::byte*:虽然地址数值相同,但bp实际指向的是一个S对象的首字节,而非std::byte数组的元素。标准并不认为"落在数组内存范围内的其他类型对象的首字节"等同于"数组的元素",因此bp不属于buf这个std::byte数组的合法指针序列,二者相减的操作违反标准要求,属于未定义行为。

gcc的-fsanitize=undefined未发出诊断,是因为UB的检测覆盖并不完全,很多场景下 sanitizer 无法识别出所有UB,不能以此判定代码合法。

正确的无UB计算方法

有两种可靠的实现方式:

方法1:通过整数类型转换计算偏移(兼容性最好)

将指针转换为std::uintptr_t(标准定义的无符号整数类型,能够完整保存指针的地址值),通过整数相减得到偏移量:

#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <new>

struct alignas(8) S { int x; };

int main()
{
    alignas(8) std::byte buf[80];
    S* const sp{new (buf + 24) S{42}};
    // ...
    const std::uintptr_t buf_addr = reinterpret_cast<std::uintptr_t>(buf);
    const std::uintptr_t sp_addr = reinterpret_cast<std::uintptr_t>(sp);
    const ptrdiff_t off = static_cast<ptrdiff_t>(sp_addr - buf_addr);
    printf("%td\n", off);
}

方法2:使用std::launder确保指针合法性(C++17及以上)

因为S是在std::byte数组的内存上构造的,而std::byte是平凡类型,原有数组元素的生命周期结束后,其内存仍可被视为std::byte类型的存储。通过std::launder可以获取指向对应std::byte数组元素的合法指针,再进行相减:

#include <cstddef>
#include <cstdio>
#include <new>
#include <memory> // 包含std::launder

struct alignas(8) S { int x; };

int main()
{
    alignas(8) std::byte buf[80];
    S* const sp{new (buf + 24) S{42}};
    // ...
    std::byte* const bp = std::launder(reinterpret_cast<std::byte*>(sp));
    const ptrdiff_t off = bp - buf;
    printf("%td\n", off);
}

内容的提问来源于stack exchange,提问作者Cole A

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 04:12:08