C++的new运算符是否保证返回的指针值不会改变?

4

在C++中:

const size_t N = 1000;
int* p = new int[N];// time=t0

我的程序只有一个线程,在为p分配内存后,程序将仅读取由p指向的内存。
标准对p的值有何规定? p在t0时刻获得的值会一直保留到delete p吗?
或者,操作系统可以自行重新分配由p指向的内存吗?
这是否取决于N的值?

7
变量的值不会自己改变。 - SLaks
4
不同的事情。 p 可以保持其值,并且p 指向的内存可以被重新分配。 - juanchopanza
@juanchopanza,操作系统可以自行决定重新分配内存吗?(无需我编写显式代码) - Alessandro Jacopson
1
如果你的内存用完或者触发了未定义行为(UB),它可能会这样做。但是,重点(没有双关语)是指针只是一个保存数字的变量。那个数字不会自己改变(除非你触发UB,在这种情况下所有的赌注都取消了)。 - juanchopanza
1
有趣的问题,我重新回来看看,因为我很好奇是否有人会找到一个标准的引用来证明它。 - Shafik Yaghmour
4个回答

5

创建后,变量p的值不能被移动。标准规定这样做是没有意义的:

3.7.4.1 分配函数

......如果请求成功,返回的值将是一个非空指针值(4.10)p0,与以前返回的值p1不同,除非该值p1随后被传递给operator delete。

(引用标准结束)


因此,如果在运行时决定将p移动到另一个地址,以便于合并一些内存片段等原因,标准规定原始p所指向的空间不能被其他内存分配所使用,这个空间将被浪费。


1
标准的内存模型和预期语义都涉及到实现必须符合的可观察行为(请参阅标准中的第1.7段- C++内存模型-至第1.10段-多线程执行和数据竞争)。这种可观察行为应与标准中描述的抽象机器的行为相匹配。它特别将程序内存定义为一组字节序列,可以通过副作用操作(对内存的I/O)进行修改。指针本身是存储在内存中的字节序列,因此受到相同规则的约束。该模型还指定了未定义行为的情况,其中任何事情都可能发生,包括内存损坏。此外,多线程程序可能会通过竞态条件(请参见1.10)“背地里”修改内存(这是我的措辞,不是真正的引用)。 new运算符通过使用分配函数工作,该函数返回内存地址,并且也必须遵守可观察行为的规则。
话虽如此,第1.9段(程序执行)在第8点中声明,在符合规范的实现中,对易失性对象的访问将严格评估抽象机器的规则。
换句话说,尽管程序应该呈现与标准描述的模型相同的结果,但只有volatile能够按照标准精确地被观察到。非易失性存储可能不会以一致的方式被观察到(如果您已经在优化代码上使用了调试器,则可以清楚地看到与程序源实际指定的内容更新相关的奇怪模式)。 另一个重要的点是new运算符是可重载的,这意味着标准描述的默认行为可能会被覆盖。如果标准的最小兼容实现(即,仅强制执行volatile变量观察)运行了一个已经被重载并且不依赖于volatile数据来跟踪其内存堆的程序,并且进行了优化和强大的内联操作,那么返回指针的值可能不会立即在存储它们的变量中可见。如果优化传递确定变量的生命周期可以从程序描述的时间缩短,那么对应于该变量的内存位置可能被重新分配并更改其内容,并且在调试器中观察到此类更改,尽管程序没有指示这样的更新。然而,编译器的工作是确保程序的执行符合预期的行为:从程序的角度来看,变量将在其整个生命周期内保持其值,并且除非程序通过内存模型允许的任何方式进行更新,否则不会更改。
在你的示例中,如果在程序中new分配之后多次访问变量p,你不会注意到自该赋值以来它所包含的值的变化,这是标准要求的。如果您尝试通过某些外部设备观察该变量的内存位置,则会跳出标准的抽象内存模型,并可能目睹其内容的更改。

0

p的值只是一个内存位置。一旦被赋值,它就不会改变。它所指向的内存可以改变,但不会被操作系统更改,只能由程序更改。如果你只是分配了一个整数数组(例如),并为每个整数赋值,那么你可以放心,除非你明确地覆盖它们(或者有一个错误这样做),否则这些值不会改变。


0
堆分配的地址将保持不变,变量不会在没有你的干预下改变它们的值。即使在删除后,你的 p 也将保持其值,但如果你尝试访问它,你将触发(希望如此)一个分段错误。变量 N 的值并不起作用。

2
我对删除后 p 的值并不确定,请参见 https://dev59.com/sm445IYBdhLWcg3wLXIK#5002201 - Alessandro Jacopson
有趣,我之前不知道! - Chiel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接