在C++中使用未指定边界的数组指针的可用情况(不是在C中)

34

考虑以下代码:

int main() {
    int (*p)[]; // pointer to array with unspecified bounds

    int a[] = {1};
    int b[] = {1,2};

    p = &a; // works in C but not in C++
    p = &b; // works in C but not in C++

    return 0;
}

在纯C中,您可以将指针分配给任何维度数组的此类型地址。但是在C ++中,您不能这样做。我发现一种情况,编译器允许将值分配给这样的指针:

struct C
{
    static int v[];
};

int main() 
{
    int (*p)[] = &C::v; // works in C++ if 'v' isn't defined (only declared)
    return 0;
}

但是找不到使用这段代码的有用案例。

有没有人能提供一个(在C++中)指向未指定边界的数组的指针的有用示例?或者这只是从C遗留下来的痕迹?


2
在C++中(而不是在C中) - Manu343726
3
“有用”大多是主观的 - 可以争论C数组很少有用(因为std :: array更好)。 - milleniumbug
1
struct C 带有静态变量有点误导人,你可以通过在命名空间范围内使用非成员 int v[]; 来表达同样的意思。 - M.M
@Matt,你能给我展示一个未指定边界的非成员数组的例子吗? - αλεχολυτ
2个回答

13
这样的指针不能参与指针运算,但仍可以使用decltype获取其类型或将其用reinterpret_cast转换为另一种指针类型或intptr_t。这是因为3.9p6节规定:类类型(例如“class X”)在翻译单元的某个时间点可能是不完整的,并且稍后变得完整;类型“class X”在两个时间点上都是相同的。数组对象的声明类型可能是不完整的类类型的数组,因此是不完整的;如果类类型稍后在翻译单元中完成,则数组类型变为完整;这两个时间点的数组类型是相同的。数组对象的声明类型可能是大小未知的数组,因此在翻译单元的某个时间点是不完整的,并在稍后变得完整;这两个时间点的数组类型(“T”的未知边界数组和“N T”的数组)是不同的类型。指向大小未知的数组或由typedef声明定义为大小未知的数组的指针的类型无法完成。5.3.1节说明:注意:对于不完整类型(除cv void之外)的指针进行间接引用是有效的。因此获得的lvalue可以以有限的方式使用(例如初始化引用);此lvalue不能转换为prvalue,请参见4.1。由于可以在不进行先前转换为rvalue的情况下对数组lvalue执行数组到指针衰减,因此代码dyp中留下的注释是正确的:
(*p)[i]

相关规则,来自4.2:

类型为“N T的数组”或“未知大小的T数组”的左值右值可以转换为类型为“指向T的指针”的prvalue。结果是指向数组第一个元素的指针。


是的 :) 我猜未知边界数组有它们的用途,而且你可以形成指向几乎所有非引用的指针。所以也许他们为了一致性而保留了它?我不太明白你用它能实现什么,这不能用指向第一个元素的简单指针完成吗?一个小点可能是它们传达了它们指向数组,而不是指向第一个元素的指针。 - dyp
@dyp:嗯,类型确实很有用,比如std::unique_ptr<int>std::unique_ptr<int[]>。但是这并不需要指向未知边界数组类型的指针,因为它应该通过特化来实现。嗯,delete p;会自动变成delete[] (int*)*p;吗?我认为会,因为*p是一个数组类型。只是我觉得对数组类型的检测只在new中进行,而不是在delete中。 - Ben Voigt
1
特别是这句话“这意味着delete 表达式的语法必须匹配 new 操作符分配的对象的类型,而不是 new 表达式的语法。”似乎表明需要通过特化来实现std::unique_ptr<T[]>。 - Ben Voigt

3
我认为编译器应该接受它(不考虑-O设置),因为静态定义可以由另一个编译单元提供。(这可能是与标准有所偏差的实用主义方法-我不是C ++专家。)发布的片段可以编译,但是它是不完整的,并且没有静态成员的定义,无法执行。

文件c.h:

struct C {
    static int v[];
};

文件 x.cpp

#include "c.h"
#include <iostream>
int main(){
    int (*p)[] = &C::v; // works in C++ if 'v' isn't defined (only declared)
    std::cout << (*p)[0] << std::endl; 
    return 0;
}

文件 y.cpp

#include "c.h"
int C::v[3] = {1,2,3};

使用(不要告诉我它已经过时了)g++ 4.3.3编译和链接。输出1。


(int*)p 是一个 reinterpret_cast。你可以使用 (*p)[0] 代替。 - dyp
@laune:确实(我添加了细节),这是标准中指定的最后一个情况(the已经声明了其大小)。 - quantdev
我将 *((int*)p) 替换为 (*p)[0] 以简化代码:你不需要进行任何类型转换。 - anatolyg

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