零长度数组的指针的属性


21

考虑

int main()
{
    auto a = new int[0];
    delete[] a; // So there's no memory leak
}

在复制初始化和删除之间,是否允许您读取处的指针a + 1

此外,该语言是否允许编译器设置anullptr


2
@Fareanor:您可以a肯定阅读(您当然不能取消引用)。
Bathsheba

8
@RasmiRanjanNayak:那真是花花公子。
Bathsheba

2
@RasmiRanjanNayak噢,我的天……不
泰德·

1
@TedLyngmo:当我说“在处读取指针a + 1”时,以下代码是否为auto b = a + 1;未定义行为?(我认为是这样)。
Bathsheba

2
@Ayxan我猜想0实际上是某些表达式的结果,直到运行时您才知道。由于new int[0]是安全的,因此可以省去一些分支/特殊情况的烦恼。试想一下,如果我是来初始化std::vectorstd::vector<int> v(0);
scohe001

Answers:


3
    auto a = new int[0];

根据[basic.compound.3],存储在a其中的值必须是以下之一:

  1. 指向对象(类型为int)的指针
  2. 指向对象末尾的指针
  3. 空值
  4. 无效

由于没有int构造类型的对象,我们可以排除第一种可能性。由于C ++要求返回非空指针,因此排除了第三种可能性(请参见[basic.stc.dynamic.allocation.2])。因此,我们有两种可能:一个超出对象末尾的指针或无效的指针。

我倾向于将其a视为过去的指针,但是我没有可靠的参考来明确地确立这一点。(不过,在[basic.stc]中对此有很强的暗示作用,请参见如何使用delete此指针。)因此,在此答案中,我将介绍两种可能性。

在复制初始化和删除之间,是否允许您读取处的指针a + 1

行为是不确定的,如[expr.add.4]所指示,无论上述哪种可能性适用。

如果a是末尾指针,则认为它指向0没有元素的数组索引处的假设元素。仅在时,才定义将整数添加j到,其中是数组的大小。在我们的情况下,为零,因此仅在is 时定义和。特别是,添加是不确定的。a0≤0+j≤nnna+jj01

如果a无效,则我们明确地陷入“否则,行为是不确定的”。(毫不奇怪,定义的情况仅覆盖有效的指针值。)

此外,该语言是否允许编译器设置anullptr

否。根据上述[basic.stc.dynamic.allocation.2]“如果请求成功,则可替换分配函数返回的值将为非null指针值”。还有一个脚注指出C ++(而不是C)需要一个非空指针来响应零请求。


1
如果它是无效的指针值,则eel.is/c++draft/basic.stc#4
TC

@TC True,这确实暗示着它不能是无效的指针值。
JaMiT

23

根据编辑问题3178的最新CWG反射器讨论,将new int[0]产生当前称为“过去”的指针值

因此a不能为null,也不能a + 1[expr.add] / 4定义


4
“由于编辑问题3178,最近的CWG反射器讨论,new int[0]产生了当前称为“过去”的指针值”,这是不准确的。没有太多讨论。有人建议该值应“指向对象末尾的指针”,由于0存在具有元素的数组,因此没有对象要超出该对象的末尾,因此该语句受到了质疑。
语言律师

@LanguageLawyer毫无疑问a + 1在任何情况下都是未定义的,所以您的观点仅涉及是否a可以为null?
MM

对于预期的语义似乎没有任何分歧,也没有针对此类指针值的当前名称适合这种情况。
TC

@MM我认为这a + 1是未定义的,也许最终的指针值将被称为“末尾指针”。我并不是说答案是错误的。这有点不准确,因为可以理解为,经过长时间的讨论并达成了共识,即仅指定结果“超出终点”就足以解决问题。“指针越过终点”的问题是它超出了某个对象的终点,并且1从这样的指针值中减去就得到了一个指向该对象的指针,而0元素数组可能不应该这样。
语言律师

2
@Bathsheba您认为关于措辞有缺陷的说法还有什么权威吗?
语言律师
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.