什么是指向数组的指针和普通数组的区别?

12
我熟悉Java并正尝试自学C/C++。我从一个公布课程材料的班级中“借鉴”了一些内容,该材料可以在这里查看。不幸的是,由于不在这个班级中,我无法向老师请教问题。我的问题出现在“动态声明数组”部分:

如果您想要能够在运行时更改数组大小,则应使用动态数组声明。这些是通过指针和new运算符完成的。关于指针的基础知识,请阅读指针部分。

使用new分配内存,然后以与静态数组相同的方式访问数组。例如,

int* arrayPtr = new int[10]; for (int i = 0; i < 10; i++) { arrayPtr[i] = i; }

内存图像与静态数组相同,但如果需要,可以更改大小。不要忘记在分配新内存之前必须释放内存(否则将会有内存泄漏)。

delete [] arrayPtr; //删除指向数组指针时需要使用[] arrayPtr = new int[50]; . . .

当您完全完成对数组的操作时,必须删除其内存:

delete [] arrayPtr;

动态多维数组与Java类似。您将会有指针指向指针。例如,请参阅

我的理解是,在C中,数组只是对数组中第一个元素的内存地址的引用。

那么,int *pointerArray = new int [10];int array[10];有什么区别(如果有的话)?

我已经进行了一些测试,看起来它们做的事情完全相同。这个网站是错的还是我读错了?

#include <cstdlib>
#include <iostream>

using namespace std;

int main(int argc, char** argv) {

    // Initialize the pointer array
    int *pointerArray = new int[10];
    for (int i = 0; i < 10; i++){

        pointerArray[i] = i;
    }

    // Initialize the regular array
    int array[10];
    for (int i = 0; i < 10; i++){

        array[i]= i;
    }

    cout << *(pointerArray + 5) << endl;
    cout << *(array + 5) << endl;

    cout << pointerArray[5] << endl;
    cout << array[5] << endl;

    cout << pointerArray << endl;
    cout << array << endl;

    return 0;
}

输出:

5
5
5
5
0x8f94030
0xbfa6a37c

我已经尝试按照网站上的描述“动态调整大小”我的指针数组,但是我的新(更大)指针数组最终被填充了0,这并不是很有用。


1
@Ivan:为什么会错呢?在指针T*上执行算术运算时,+ 5表示+ 5 * sizeof(T) - Matthieu M.
1
在引用的文本中提到了这一点,但你仍然忘记使用 delete[] pointerArray 释放内存。 - mkaes
5
啊,这不是C++,简直是折磨。在C++中,我们有 vector 类来表示动态数组。有一种观点认为,教授C++的目标是通过只教授所有最糟糕的方法来使学生尽可能地产生错误。鉴于你找到的资料遵循这种理念,如果你想学习C ++,最好的选择是尖叫着逃跑,然后去购买一本C++的书籍。 - jalf
2
我就是不明白为什么这些教程非要毁掉C++的形象。在C++中,有一个比std::vector更好的替代品,但他们还是谈论使用new[]创建动态数组。 - Naveen
1
@Sriram:我们质疑的不是问题的合法性。我们说的是他找到的教材很糟糕,会浪费他的时间,并需要他之后重新学习很多东西才能掌握正确的知识。 - jalf
显示剩余11条评论
9个回答

16

int array[10];声明数组大小是静态的,这意味着它是固定的——这是唯一的主要区别。它也可能被分配在函数的堆栈帧内,即程序的堆栈上。对于这种类型的数组,您不需要担心使用delete[],实际上,如果您delete它,可能会导致程序崩溃。

当使用operator new时,您动态分配内存,这可能会更,而且内存通常来自于而不是程序的堆栈(虽然并非总是如此)。在大多数情况下,这更好,因为您在堆栈空间中的限制更多。但是,当您不再需要它时,必须注意内存泄漏并delete[]您的内容。

至于你的数组填充为零,你的课程材料没有说到的是你必须这样做:

int *arr = new int[20]; // old array
//do magic here and decide that we need a bigger array
int *bigger = new int[50]; // allocate a bigger array
for (int i = 0; i < 20; i++) bigger[i] = arr[i]; // copy the elements from the old array into the new array
delete[] arr;
arr = bigger;

这段代码通过增加30个元素来扩展数组arr。请注意,您必须将旧数据复制到新数组中,否则它将不存在(在您的情况下,所有数据都将变为0)。


那么这里的魔法部分是“arr = bigger;”吗?否则,如果不使用指针,我可以将所有内容复制到bigger并使用bigger而不是arr。 - James T
否则一切都非常清晰。非常好的回答。谢谢! - James T
是的,神奇的部分就是指针重新赋值。你可以使用旧名称并增加更大的大小! - evgeny
答案中的代码缺少数组数据的初始化。bigger[20]bigger[49]的元素未初始化,将包含随机值。要解决此问题,您应该使用:bigger = new int[50]();(注意多余的括号),对于堆栈分配的数组:int initialized[20] = {}; - David Rodríguez - dribeas

10
我理解的是,在C语言中,数组仅仅是对数组中第一个元素内存地址的引用。那么,如果有的话,int *pointerArray = new int[10] 和 int array[10]之间有什么区别呢?
你提到的是C/C++初学者经常感到困惑的原因。
在C/C++中,数组对应于足够大以容纳所有元素的内存块。这与方括号[]语法相关,就像你的例子一样:
int array[10];

C/C++的一个特性是可以通过指向其类型的指针引用数组。因此,您可以编写以下内容:

int* array_pointer = array;

这与以下代码相同:

int* array_pointer = &array[0];

这使得可以像通常访问数组元素一样访问数组指针:array_pointer[3],但是你不能将array视为指针,像在指针上进行指针算术运算一样(即array++会失败)。
话虽如此,也有事实证明,你可以完全不使用[]语法来管理数组,只需使用malloc分配数组,然后使用原始指针来操作。这就是C/C++的"美"所在。
总之:必须区分指针和它指向的内存(实际数组):
  1. 声明中的[]语法(例如,int array[10];)同时引用两个方面(它给你一个指针和一个数组);

  2. 当声明一个指针变量(例如,int* p;)时,你只得到了指针;

  3. 当评估一个表达式(例如,int i = p[4];array[4];)时,[]只是表示解引用指针。

除此之外,int *pointerArray = new int[10];int array[10];之间唯一的区别是前者是动态分配的,后者在堆栈上分配。

int *a和int *a[10]之间有什么区别? - Suraj Jain
@SurajJain int *a 是指向整数的指针,而 int *a[10] 是一个指向整数指针的数组(大小为10)。 - Andreas K.

3

动态分配:

int * pointerArray = new int[10]; 

[顺便说一下,这是一个指向包含10个整数的数组的指针,而不是指针数组]

静态分配(可能在堆栈上):

int array[10]; 

否则它们是相同的。

3
从Java转到C/C++时,理解C/C++数组的问题在于C/C++区分数组变量和用于存储数组内容的内存。这两个概念都很重要且不同。在Java中,你只是引用了一个数组对象。
你还需要了解C/C++有两种分配内存的方式。内存可以在堆上或栈上分配。而Java没有这种区别。
在C和C++中,数组变量是指向数组第一个元素的指针。数组变量可以存在于堆或栈上,包含其内容的内存也可以存在于堆或栈上,并且它们可能是不同的。你的示例是int数组,因此可以将数组变量视为int*。
int *pointerArray = new int[10]; 和 int array[10]; 之间有两个区别:
第一个区别是,第一个数组包含内容的内存是在堆上分配的。第二个数组更加棘手。如果array是函数中的局部变量,则其内容在栈上分配,但如果它是类的成员变量,则其内容会被分配到包含对象所在的位置(堆或栈)。
第二个区别是,正如你已经意识到的那样,第一个数组是动态的:其大小可以在运行时确定。第二个数组是固定的:编译器必须能够在编译时确定其大小。

2
首先,我建议您寻找其他学习C++的资源。您提到的页面非常令人困惑,与实际编写C++程序的方式关系不大。在C++中,大多数情况下,您会使用std::vector来代替数组,而不是所提到页面上复杂的解决方案。实际上,在实践中,您几乎不会使用operator new[](即数组new)。
事实上,std::vector在某些方面更像Java中的ArrayList而不是简单的数组;与Java中的数组不同,您可以通过在末尾插入元素来轻松扩展向量。它还支持迭代器,虽然C++迭代器与Java迭代器有很大的不同。另一方面,您可以使用[]运算符访问它,就像普通的数组一样。
页面上描述的数组通常称为C风格数组。在C++中,它们的使用大多限于具有静态生命周期的对象,尽管它们偶尔会出现在类中。无论如何,它们从不被动态分配。

0

一方面:

int ar[10];

使用在堆栈上分配的内存。您可以将其视为本地可用,虽然可能会将指针/引用传递给其他函数,但是一旦超出范围(在您的示例中,在主方法结束时,但通常不是这种情况),内存将被释放。

另一方面:

int ar* = new int[10];

在堆上分配数组的内存。它可用直到整个程序退出或使用删除。

delete[] ar;

请注意,仅当相应的新内容也有它们时,才需要“[]”来进行删除操作。

0

主要区别在于指针允许的某些操作在数组上是不允许的。


0

有一些区别,但不在你指向的区域。*pointerArray 将指向一个大小为 10 字节的内存块的开头。array 也是如此。唯一的区别将是它在内存中存储的位置。pointerArray 是动态分配的内存(在运行时),因此将进入堆,而array[10] 将在编译时分配,并进入堆栈。


0

确实,您可以通过使用指向其第一个元素的指针来获得大多数数组功能。但编译器知道静态数组由多个元素组成,最显着的区别是 sizeof 运算符的结果。

sizeof(pointerArray) = sizeof int*

sizeof(array) = 10 * sizeof int


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