zoukankan      html  css  js  c++  java
  • 数组与指针之间关系的理解

    本篇讨论数组与指针之间的关系,分别以一维数组与二维数组为例进行说明。

    一. 一维数组。

    首先,让我们明确以下两点:

    第一,数组名是一个固定的东西,它只能代表一个数组,也就是说,不允许这个数组名在后面又去表示另一个数组。

    第二,数组名是一个指针,并且是一个常量指针。

    现有一条语句: array[2] { 0, 1 },这条语句定义了一个数组,数组名是 array,该数组包含 2 个元素,其值分别为 0 和 1。我们访问数组元素通过[ ] 运算符,比如 array[ 0 ] ,它的值就是 0 。这个 array ,也就是数组名,实质上是一个指针,它指向这个数组第一个元素的首地址。通过下面代码,我们可以清楚的知道,array 的地址和 array[ 0 ]的地址是一样的。

     1 #include <iostream>
     2 using namespace std;
     3 
     4 int main() {
     5     int a[2] { 0, 1 };
     6     cout << "a address : " << &a << endl;
     7     cout << "a[0] address : " << &a[0] << endl;
     8     
     9     return 0;
    10 }

    二. 二维数组

    二维数组,我们可以把它看作一个矩阵,由行列组成。现有一条语句 array_2d [ 2 ] [ 3 ] ,这条语句声明了一个二维数组,它有两行三列共 6 个元素。我们换一个角度,不要把它看成一个整体,而是看成 2 个 一维数组的组合,每个一维数组有 3 个元素。这两个一维数组的首元素分别是 array_2d [ 0 ] [ 0 ] , array_2d [ 1 ] [ 0 ]。为了更清楚的查看结果,请看以下代码:

     1 #include <iostream>
     2 using namespace std;
     3 
     4 int main() {
     5     int a[2] { 0, 1 };
     6     int b[2][3] { { 1, 2, 3 }, { 3, 2, 1 } };
     7     cout << "a address : " << &a << endl;
     8     cout << "a[0] address : " << &a[0] << endl;
     9 
    10     cout << "b address : " << &b << endl;
    11     cout << "b[0] address : " << &b[0] << endl;
    12     cout << "b[1] address : " << &b[1] << endl;
    13     cout << "b[0][0] address : " << &b[0][0] << endl;
    14     cout << "b[1][0] address : " << &b[1][0] << endl;
    15 
    16     return 0;
    17 }

    这段代码的运行结果如下:

    1. a 和 a[ 0 ] 的地址一样。

    2.  b 与 b[ 0 ] 以及 b[ 0 ][ 0 ]这三者的地址一样。

    这表明数组名是数组首元素的地址,不论数组是一维还是二维,都遵循这个规律。

    三. 当动态分配内存时,数组名与首元素地址是什么关系?

    很多时候,之间定义固定长度的数组,并不能达到我们想要的效果,因此需要动态分配内存。请看如下代码:

     1 #include <iostream>
     2 using namespace std;
     3 
     4 int main() {
     5     int* new_array = new int[5];
     6     int** new_array_2d = new int* [5];
     7     for (int i = 0; i < 5; ++i) {
     8         new_array_2d[i] = new int[5];
     9     }
    10 
    11     cout << "New_array address : " << &new_array << endl;
    12     cout << "New_array[0] address : " << &new_array[0] << endl;
    13     cout << "New_array_2d address : " << &new_array_2d << endl;
    14     cout << "New_array_2d[0] address : " << &new_array_2d[0] << endl;
    15     cout << "New_array_2d[0][0] address : " << &new_array_2d[0][0] << endl;
    16 
    17     return 0;
    18 }

    在第 5 行,我们动态分配了一个元素个数为 5 的数组, new 运算符返回了首地址,并赋值给指针 new_array,现在 new_array 可以理解为该数组的名字。数组元素也可以通过 [ ] 运算符来访问。

    但是代码的运行结果如下:

    1. new_array 的地址不是 new_array [ 0 ] 的地址。

    2. new_array_2d 的地址, new_array_2d [ 0 ] 的地址, new_array_2d [ 0 ] [ 0 ] 的地址,互不相同。

    通过上面的结果我们可以清楚的知道,在动态分配内存时,数组名(即指针)的地址,与数组元素的地址无关。

    代码中有几处需要解释:

    第一,new 运算符返回的是一个地址,因此需要用一个指针变量保存。

    第二,new运算符分配连续空间时,指针同样保存的是首地址。

    第三,在二维数组动态分配的时候,用到了二级指针,因此你会看到 int** x这种写法。指针变量里存储的是地址,当这个指针指向的数据是 int 型,就写成 int* x,而当这个指针指向另外一个指针时,自然就应该写成 int** x(可以将 int 和第一个星号看成一个整体,表示指针数据类型,将第二个星号和变量名看成普通的变量声明即可)。前面说过,可以将二维数组看成行列的组合,那么在动态分配二维数组时,先将所有行的地址,当作一个竖着的数组,这个竖着的数组的首地址,赋值给二维数组的名字(即第 6 行定义的二级指针 x ),然后再循环地为每个行(可以看成横着的数组)分配空间。这个竖着的数组保存了所有首地址,其中的每个元素分别指向每一个横着的数组,每个横着的数组保存的真正的元素值。

    第四,由三可知,第 6 行代码右边就是竖着的数组,其中的每个元素都是指针,因此这个数组是指针数组,应该用 new int* [ ]。而第 7 行到第 9 行里循环分配的空间是横着的数组,里面每个元素是一个整数,因此写为 new int [ ] 。

  • 相关阅读:
    关于JAVA的线程问题
    Java 对JTextField添加回车响应
    Failed to install *.apk on device 'emulator-5554': timeout .
    静态属性
    类与对象的实例属性

    面向对象2
    面向对象设计
    re模块,主要用来查询
    xml对标签操作,
  • 原文地址:https://www.cnblogs.com/Hello-Nolan/p/12308607.html
Copyright © 2011-2022 走看看