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

C++连续非数组对象指针运算的标准合规性疑问

关于C++20前std::vector实现与指针算术合法性的疑问

通常业界普遍认为,C++20标准正式发布前,开发者无法写出完全合规的std::vector实现(相关讨论可参考标准提案P0593):如果要满足性能要求,就不能将元素直接存放于声明为元素类型的内部数组中,此时对返回的元素指针(尤其是.data()返回的指针)做指针算术运算属于未定义行为,典型的问题示例如下:

char * storage = new char[sizeof(int) * 3];
int * data = new(storage) int(1);
new(storage + sizeof(int)) int(2);
new(storage + sizeof(int) * 2) int(3);

data[2] = 5; // 未定义行为:data指向的地址处不存在int类型数组

但C++17之前的标准文本中,存在如下三条可作为推导依据的条款:

[basic.compound] 若类型为T的对象存储于地址A,则值为地址A的cv限定T*类型指针即指向该对象,与指针值的获取方式无关。
[expr.add] 对于指针加减运算符,指向非数组对象的指针,与指向长度为1、元素类型为该对象类型的数组首元素的指针行为完全一致。
[expr.add] 此外,若表达式P指向某数组对象的最后一个元素,则表达式(P)+1指向该数组对象尾元素的后一位置;若表达式Q指向某数组对象尾元素的后一位置,则表达式(Q)-1指向该数组对象的最后一个元素。

按照上述条款推导,有观点认为如下代码是完全合法、符合标准要求的:

char * storage = new char[sizeof(int) * 3];
int * data = new(storage) int(1);
new(storage + sizeof(int)) int(2);
new(storage + sizeof(int) * 2) int(3);

int * tmp = data + 1; // 依据[expr.add]该操作合法
                      // 依据[basic.compound],tmp此时指向storage + sizeof(int)位置构造的int对象
                      // C++17及以上版本需要调用launder进行指针清洗
tmp = tmp + 1;        // tmp此时指向storage + sizeof(int) * 2位置构造的int对象
*tmp = 5;

(data + 1)[1] = 5;    // 与上述操作等价

该推导的核心结论是:对于连续存储的多个同类型对象,只要不直接使用大跨度指针偏移,而是通过逐次步进1个位置的方式移动指针,就可以合法访问相邻的同类型对象。该理解是否符合C++标准的规则本意?

补充说明:C++17标准调整了[basic.compound]和[expr.add]的条款表述,要求每次步进移动指针后都需要对指针做launder清洗,才能正确指向对应位置的int对象,除此之外规则逻辑与此前版本等价。


编辑说明:已移除代码中reinterpret_cast的使用,其使用合法性不属于本问题的讨论范围。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 02:54:24