c++oding="utf-8" ?>
std::span构造时传入nullptr且size>0会直接触发未定义行为;必须显式校验ptr!=nullptr,subspan参数需手动验证offset+count≤size,operator[]和at()在release下均无边界检查,真正防护依赖手动索引校验。

std::span构造时传入空指针会直接UB
std::span的构造函数不检查指针是否为空,也不验证长度是否合理。传入nullptr且size > 0,构造即触发未定义行为——常见现象是调试器停在__builtin_assume或直接访问违规(AV),但编译器几乎不报错。
排查关键点:
- 所有从裸指针构造
std::span的地方,必须显式校验:if (ptr == nullptr || size == 0) throw std::invalid_argument("null span"); - 第三方C库返回的缓冲区(如
recv()、mmap())要先判空再构造,别信“文档说非空” - 用
std::span<const std::byte></const>替代std::span<char></char>可减少类型别名误用,但不解决空指针问题
subspan越界不报错,只静默返回空视图
buf.subspan(offset, count)在offset + count > buf.size()时不会抛异常,也不会assert,而是返回一个size == 0的空std::span。后续调用.at(0)才抛std::out_of_range,但协议解析逻辑早已走偏。
典型错误写法和修复方式:
- ❌
buf.subspan(4, payload_len)——payload_len来自报文头,必须提前校验:if (4U + payload_len > buf.size()) { /* truncated */ } - ❌
buf.subspan(4, buf.size() - 4)——若buf.size() ,<code>size_t减法溢出,结果极大,subspan返回空视图,逻辑静默失败 - ✅ 统一用显式边界判断:
if (buf.size() >= offset && buf.size() - offset >= count)
operator[]越界在Release下完全静默,不是崩溃而是读脏数据
std::span::operator[]和裸指针行为一致:不校验索引,越界即UB。Release模式下at()也等价于operator[](GCC/Clang默认无调试检查),所以依赖at()防越界是靠不住的。
真正有效的防护只有两种:
- 手动校验:
if (i - 用
std::span::at()仅限Debug构建,并配合_GLIBCXX_DEBUG(libstdc++)或/D_ITERATOR_DEBUG_LEVEL=2(MSVC) - 禁止写
buf.data()[i]——它绕过std::span的所有语义约束,比operator[]更危险
栈数组包装成std::span后传出作用域就是悬垂
常见错误是把局部栈数组地址传给std::span并返回:
- ❌
char buf[4096]; recv(fd, buf, sizeof(buf)); return std::span<const std::byte>(buf, n);</const>——buf出函数即销毁,返回的std::span立即悬垂 - ✅ 正确做法:用
std::vector<:byte></:byte>作为类成员或函数局部变量,用实际接收字节数n构造std::span,确保底层内存生命周期覆盖所有使用点 - ⚠️ 若必须用裸指针(如
mmap),请用std::unique_ptr<:byte custom_deleter></:byte>管理,再通过.get()和真实长度构造std::span
最常被忽略的是:std::span本身不提供任何运行时安全担保,它的“安全”完全建立在你每一步都主动校验的基础上——构造、切片、访问,三处缺一不可。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











