C++ - 指向数组的指针 - 指针数组

3

我注意到这引起了几个人的困惑,但在阅读了一些帖子和cplusplus教程后,我的脑袋还是糊涂的。

假设我有以下变量在头文件中 -

int numberOfLinePoints;
D3DXVECTOR3* line;   //confused as to what it is

然后在C++实现文件中,我将它们初始化如下 -

//both initialized in constructor
numberOfLinePoints = 25;
line = new D3DXVECTOR3[numPoints];   //array of pointers?

现在我的line变量代表什么?

据我从stackoverflow上的链接所了解,它应该代表一个指针数组。然而,我接着读到了以下内容...

(1) 初学者指针

...其中(A)讨论了指针数组和(B)数组指针,这让我再次感到困惑,因为它们似乎都工作得很相似。

我定义指针的位置与我分配它们的位置不同,这似乎是我困惑的根源。 我正确地认为这是一个指向D3DXVECTOR3对象的指针数组吗?

最后 - 如果变量line保存有关一条线段的信息,那么如何创建一组线段的数组? 我目前有以下内容-

//HEADER FILE
int numberOfLineSegments;
D3DXVECTOR3** lineCollection;   //array of pointers - each of which
                                //points to an array of pointers?
//CPP FILE
numberOfLineSegments = 8;                            //constructor
for(i = 0; i < numberOfLineSegments; i++)            //initialization
{                                                    //and allocation  CORRECT?
   lineCollection[i] = new D3DXVECTOR*[numPoints];   //of memory for     Y/N
}                                                    //lineCollection

VOID createLineSegments(startPoint, endPoint) //could return array instead
{
//pseudo to generate one line segment
while != numberOfLinePoints
line[sentinel++] = interpolate(startPoint, endPoint, time_T)

//pseudo to generate array of line segments
while != numberOfLines
lineCollection[sentinel++] = line
}

任何帮助都非常感激。

1
你永远不应该在头文件中声明变量的存储。你的“HEADER FILE”行需要全部放在CPP文件的顶部。头文件用于声明事物存在的抽象潜力,而不是它们的实际存储,或者它们在实际程序中被使用或存在的现实。 - Mordachai
我不知道这个。在我的课程中,我一直被教导对象的声明应该放在对象头文件中,并在 CPP 文件中初始化。我会向其中一位导师提出这个问题,谢谢。 - Drew_StackID
6个回答

4

你的第一个例子:

int numberOfLinePoints;
D3DXVECTOR3* line;   //confused as to what it is

声明一个指向D3DXVECTOR3的简单指针。指针可以通过两种方式初始化。第一种:

line = new D3DXVECTOR3;

这会创建一个单独的D3DXVECTOR3对象,并使line指向该对象。第二点:

line = new D3DXVECTOR3[numberOfLinePoints];

这将创建一个D3DXVECTOR3数组,并使line指向该数组的第一个元素。接下来,您可以使用指针算术运算访问该数组的其他元素。

如果将指针声明为双指针:

D3DXVECTOR3** line;

您只需创建另一层间接引用即可。

3
int numberOfLinePoints;
D3DXVECTOR3* line;   //confused as to what it is
//both initialized in constructor
numberOfLinePoints = 25;
line = new D3DXVECTOR3[numPoints];   //array of pointers?

line是一个D3DXVECTOR3数组。如果D3DVECTOR3本身是一个指针,那么它将是一个指针数组。然而,由于我不太熟悉C++ D3D头文件,所以我不确定。

D3DXVECTOR3** lineCollection;

这是一个指针数组,每个指针很可能指向一行(即,D3DXVECTOR3数组)。
您有两个选择。从内存角度而言,最好的方法是将lineCollection中的每个条目设置为指向相应线条的指针。如果您知道这些线不会改变(并且不会被释放),或者如果它们发生更改,您希望立即在集合中反映出这些更改,那么这是安全的。
另一种选择是为lineCollection中的每个条目创建一个新数组,并将每个line中的点复制到此新数组中。
没有正确答案,这取决于您想要的功能。

谢谢Donnie,这正是我想知道的。其他回复也很有帮助,但这可能是我能理解的最清晰的回复了。 - Drew_StackID

3

尝试简洁回答第一个问题,不引入其他问题。

C++(和C)使用数组中单个项目的指针作为完整数组的句柄。然而,有些指针并不指向数组中的项目!您必须自己区分指向单个项目和指向数组中项目的指针。

int length = 8;
D3DXVECTOR3* line = new D3DXVECTOR3[length];

新的[]运算符返回分配的数组中第一个项目的指针,然后将该值分配给line。请注意,因为指针不区分单个项和数组中的项:
- 您必须单独存储长度 - 您必须小心使用正确的指针索引(如上面的“line”) - 最好使用“真正”的容器类型,例如:
- std::deque、std::vector等 - std::tr1::array(也称为boost::array) (最后一条要点并不意味着您永远不使用指针,而是在这些容器更合适时不使用它们。)

2
D3DXVECTOR3 line; // Line is a D3DXVECTOR3
D3DXVECTOR3 * line; // Line is EITHER a pointer to D3DXVECTOR3 OR an
                    // array of D3DXVECTOR3
D3DXVECTOR3 ** line; // Line is an array of pointers to D3DXVECTOR3 OR
                     // an array of array of D3DXVECTOR3

这是因为数组在内存中没有特定的结构。它只是一系列D3DXVECTOR3依次排列。所以指向第一个元素,你就可以访问所有其他元素。
因此,拥有:
D3DXVECTOR3** lineCollection; // An array of pointers OR an array of array!
new D3DXVECTOR[numPoints]; // A pointer to an array of D3DXVECTOR
lineCollection[i] // A pointer to an array

您可以通过以下步骤进行初始化:

lineCollection[i] = new D3DXVECTOR[numPoints]; // No extra *

然而:尽量使用STL(如std::vector)而不是丑陋的C / Java风格数组。如果可以的话,避免在堆上声明(使用“new”),而是在栈上声明:

D3DXVECTOR a, b, c; // a, b, and c ARE D3DXVECTOR, not pointers
std::vector<D3DXVECTOR> lines;
lines.push_back(a);
lines.push_back(b);
lines.push_back(c);

// equivalently: (you can push_back without temporaries)
std::vector<D3DXVECTOR> lines;
lines.push_back(D3DXVECTOR());
lines.push_back(D3DXVECTOR());
lines.push_back(D3DXVECTOR());

这样可以避免手动内存管理,使代码更易读。但是,您可能无法始终使用此便利(根据代码组织方式)。如果有人提到性能问题,请暂时不用担心。首先确保没有段错误或内存泄漏,再考虑性能。


据我所知,编译器可以决定在使用new时将对象分配到堆栈上,如果程序行为没有改变的话。只是顺便提一下。 - Björn Pollex
我错过了这篇帖子。实际上,这篇帖子和Donnie的帖子都对我有很大帮助。可惜我不能设置两个正确答案。谢谢。 - Drew_StackID
很高兴能够帮助。@Space_cowboy:我对编译器优化不是很了解,我正在寻找一些好的术语来明确地告诉名称两种分配方式,因为我更关心程序员的角度。堆和栈会让新用户(显然还包括高级用户)感到困惑。这里有什么建议吗? - Tristram Gräbener
这是我迄今为止做的最"冒犯"的编辑,但是"stl::"明显是一个错误。(STL是一个特定的库,不仅仅是C++标准库,甚至不仅仅是其中的模板。)如果这太过分了,我向你道歉。(在此期间还进行了其他小的调整。)关于性能:虽然我同意如果程序“不能正常工作”,那么它运行得有多快并不重要,但是在这里没有性能差异!vector就是一个动态数组,就像你使用new[]创建的一样,唯一的区别是接口。(你甚至可以将&v[0]作为C风格数组传递。) - Roger Pate
Space_C0wb0y:虽然在标准中使用“as-if规则”是真实的,但在实践中安排起来非常困难,我不知道有任何试图这样做的实现。(编译器必须进行全局分析,以确保您不编写自己的operator new,例如。)如果有这样的实现,我会非常感兴趣看看它们是如何做到的。 - Roger Pate
@Roger Pate:没问题,你做得对! :) 只可惜我没有仔细检查自己的写作... 至于性能方面,我只是不想涉及到我经常见到的任何性能或有限堆栈大小考虑;你又说对了 ;) - Tristram Gräbener

1
line = new D3DXVECTOR3[numPoints];

line 保存了 D3DXVECTOR3 数组的第一个元素的内存地址。

也就是说,line 是指向数组第一个元素的指针。

本文 可以进一步阐明这个概念。


0

看看这个简单的例子:

情况1:

int *p = new int [N];

这里p是指向N个整数数组的指针,p存储了该数组的起始地址。

第二种情况:

int **p = new int *[N]; //p is a pointer to pointers holding integers used for 2D array.


for(int i=0 ; i<N ; i++)
{

    p[i] = new int [N]; // each element is pointer to array of integers.

}

适用于所有类型的用户定义。


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