0%

C++之数组

数组是连续分配的非空的具有特定元素对象类型的对象集合, 组成数组的对象称为该数组的元素. 数组类型由元素类型与元素的数目确定.

相对于整型,浮点型和字符型数据, 数组是最简单最基本的结构型数据类型. 数组名作为数组实体的标识符,具有特殊性,不同于整型、浮点型、指针型或结构类型等变量标识符。这是因为数组是一组元素的聚集,不能把一个聚集看作一个值直接读出(即右值),也不能把一个聚集看作一个地址直接赋值(即左值).

数组定义与初始化

数组的定义

声明数组的一般形式如下:

数据类型 数组名[常量表达式1][常量表达式2]...[常量表达式N];

数组名是一个常量, 代表数组元素在内存中的起始地址, 也就是数组中第一个元素的地址. 数组可以是一维数组, 也可以是N维数组. 上述声明中的”常量表达式N”称为下标表达式, 且必须是一个正整数. 数组的下标限定了数组中元素的个数, 也用来标识数组中的元素. 下标从0开始计算. 例如:

int a[10];
//表示数组a中有10个元素, a[0]到a[9].

此外, 数组的大小必须在定义时就固定, C++不存在动态定义大小的数组, 例如下面的定义是错误的:

int num;
cin>>num>>endl;
int a[num];

  /*
 C 和 C++ 是两种不同的语言。
 C 自 C99 开始,在特定条件下允许使用变量作为数组长度定义数组。
 C++ 至今不允许这种用法。
 */		

一维数组的初始化

一维数组初始化的几种方式:

//1. 定义数组时对数组元素逐个赋值
 int a[5];
for (int i = 0; i < 5; i++) {
    a[i] = i;
}
    
//2. 将数组元素的初始值依次放在一对大括号内:
int a[5] = {0,1,2,3,4};

//3. 当数组容量较大不能逐一赋值时, 可以仅对其中一部分元素进行赋值, 其余元素将默认为0. 下面的代码仅对前3个元素进行了赋值:
int a[50] = {0,1,2};

//4. 可以对数组中所有元素统一赋一个初值:
int a[5] = {0};

//5. 如果对全部元素进行了赋值, 则可以省略数组长度:
int a[] = {0,1,2,3,4};

C++并不提供对数组的边界检查, 因此要特别注意越界访问的处理.

二维数组的初始化

另一种常用的数组时二维数组, 相当于平面内的一个二维矩阵. 下面的代码定义了一个二维数组:

float array[2][3];
//表示2行3列, 即第一个参数表示行数, 第二个参数表示列数. 下标仍然从0开始计算.	

下面的代码是二维数组的几种初始化方式:

//1.对元素逐一赋值
float array[2][3] = {1,2,3,4,5,6};
//更直观易懂的写法是:
float array[2][3] = {{1,2,3},{4,5,6}};

//2. 对于数组容量比较大的情况, 可以对部分元素进行赋值:
float array[2][3] = {{1},{4}};
//上面的语句只对每行首列进行了赋值, 其他元素被默认赋值为0

//3. 当然也可以选择某行中的任意一列或多列来赋值:
float array[2][3] = {{1},{0,4}};

//4. 也可以挑中器重的某行,对其他行进行赋值:
float array[2][3] = {{},{0,4}};

//5. 如果初始化时对二维数组里的全部元素进行了赋值, 则可以省略第一维的长度, 但第二维的长度不能省略:
float array[][3] = {1,2,3,4,5,6};

可以通过二维数组的用法推广到更高维数组的用法.

数组和指针

任何存储在内存中的变量都有地址, 数组有地址, 数组中的元素也有地址. 所以使用指针也能访问数组中的元素.

#include <iostream>
using namespace std;
int main(int argc, const char * argv[]) {

    int a[5] = {0,1,2,3,4};
    int *p = &a[0];
    cout << a[0] << endl;
    cout << &a[0] << endl;
    cout << &a << endl;
    cout << a << endl;
    cout << p << endl;
    cout << *p << endl;
    
    return 0;
}

输出:
0
0x7fff5fbff7c0
0x7fff5fbff7c0
0x7fff5fbff7c0
0x7fff5fbff7c0
0

用指针访问数组时比较高效的, 它无须每次都重新计算地址, 而使用下标符号[]每次都需要进行变址寻址,这样的时间消耗在数组容量较大时是不可忽略的.

在C++中, ++运算和*运算具有相同的优先级, 且运算结合的方向是自右向左, 所以*p++*(p++)的作用是完全相同的, 即首先得到p所指向的内容,再将p向下移动一位(指向下一个元素).(*p)++则表示p所指向的内容加1.

特别地, 可以通过指针运算改变指针变量的值, 但是不能将同样的操作运用于a, 即不存在a++之类的运算.因为a表示的数组首元素的地址, 是一个指针常量.

还需要注意的是, 指向数组的指针并不会做越界检查.

二维数组可以看做一个特殊的一维数组, 内存中二维数组也是按行收尾相接线性排列的. 例如可以使用下面的代码遍历一个二维数组:

#include <iostream>
using namespace std;
int main(int argc, const char * argv[]) {

    
    int a[][3] = {0,1,2,3,4,5,6,7,8};
    int *p = &a[0][0];
    for(int i = 0; i < 9; i++){
        cout << *p++ << endl;
    }
    return 0;
}