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

C++17中地址与类型正确的指针是否始终有效?标准修改解读咨询

C++17标准修改的解读与补充规则说明

你的解读完全正确,咱们来详细拆解这个问题,同时看看有没有其他规则能覆盖原来语句的部分场景:

一、C++17之前的规则逻辑

在C++17之前,[basic.compound]/3的那条规则是核心:只要指针的值是某个对象的地址,且指针的cv-qualified类型和对象类型匹配,不管这个指针是怎么来的,都被认为指向该对象。

放到你的示例代码里:

alignas(int) unsigned char buffer[2*sizeof(int)];
auto p1=new(buffer) int{};
auto p2=new(p1+1) int{};
*(p1+1)=10;

p1指向buffer中第一个int对象,p1+1的地址刚好是p2指向的第二个int对象的地址,而且p1+1的类型是int*,和p2指向的对象类型完全匹配。按照旧规则,*(p1+1)完全合法,行为是确定的——就是修改p2指向的那个int对象的值。

二、C++17之后的语义变化

C++17移除了这条规则,核心的语义变化在于:指针的“指向”不再单纯由地址和类型决定,还要看指针的来源和语义。

在你的示例里,p1是指向单个int对象的指针(不是数组元素的指针),所以p1+1是这个单个int对象的尾后指针。根据C++17及之后的规则,尾后指针只能用于比较(比如和其他指针比较地址),绝对不能被解引用——哪怕它的地址刚好对应另一个合法对象,解引用操作依然属于未定义行为。

这时候哪怕p1+1的地址和p2完全重合,编译器也不认为p1+1指向p2的对象,因为p1+1的语义是“第一个int的尾后”,而非“第二个int的起始”。

三、有没有其他规则弥补?

虽然那条规则被移除了,但C++标准里还有一些相关规则可以处理类似的场景,但它们不是直接替代,而是有严格的适用条件:

  • 对象存储位置的指针转换:如果一个指针指向某个存储位置,而该位置已经通过placement new创建了兼容类型的对象,你可以通过reinterpret_cast将该存储位置的指针(比如unsigned char*类型的buffer指针)转换为int*来访问对象。比如你可以直接用reinterpret_cast<int*>(buffer)+1来访问第二个int,这是合法的。
  • std::launder的适用场景:如果原来的指针指向的对象已经结束生命周期,且在同一地址创建了新对象,std::launder可以让指针指向新对象。但在你的示例里,p1指向的对象还活着,所以std::launder在这里不适用。
  • 数组语义的指针:如果你一开始就把buffer当作int数组的存储(比如int* arr = reinterpret_cast<int*>(buffer);),那么arr+1就是数组第二个元素的指针,解引用完全合法——因为这时候指针的语义是数组元素的指针,而非单个对象的尾后指针。

要注意的是,这些规则都是有边界的,不能像旧规则那样“只要地址和类型对就合法”,而是需要严格符合指针的语义来源。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 06:53:21