首页 > 编程笔记 > C++笔记 阅读:1

C++数组用法详解(附带实例)

数组是编程中最基础且最常用的数据结构之一,它可以存储多个同类型的元素。

数组是一种线性数据结构,可以存储固定数量的同类型元素。数组中的元素在内存中是连续存储的,每个元素都有一个索引,用于唯一标识该元素,索引(下标)从 0 开始。

例如,我们可以声明一个包含 5 个整数的数组(见下图):
int arr[5];

图 1 包含5个整数的数组

在这个数组中,arr[0] 是第一个元素,arr[4] 是最后一个元素。

C++开辟数组空间

1) 数组的声明

在 C++ 中,我们可以在声明数组时确定其大小。例如,声明一个包含 10 个整数的数组:
int arr[10];
注意,数组的大小必须是常量,不能使用变量来确定数组的大小。如果想创建一个可变长度的数组,可以使用 STL 中的 vector 容器。

在不同位置开辟的数组,其初始值也有所不同。在栈区(即函数内部)开辟的数组,数组元素的初始化值是未定义的,可能是随机混乱的值或内存中残余的垃圾值。而在堆区(即全局或静态区)开辟的数组,所有元素会自动将每字节都归零(即自动初始化为 0)。

示例代码如下:
const int N = 10;   // 全局变量,a = {0, 0, 0, 0, ...}(默认初始化为全 0)
int a[N];

int main() {
    int b[N];       // 局部变量,b = {随机值}(一些随机的值)
    return 0;       // 返回 0 表示程序正常结束
}
上述代码首先定义了一个常量整数 N,其值为 10。然后,在全局作用域中声明了一个名为 a 的整型数组,其大小为 N(即 10)。由于 a 是全局变量且未显式初始化,因此数组 a 的所有元素都被默认初始化为 0。

在 main() 函数内部,声明了一个名为 b 的整型数组,大小同样为 N。与全局变量 a 不同,局部数组 b 没有被显式初始化,因此它的初始值是不确定的。这些值可能是内存中的任意垃圾值,也可能是编译器自动分配的值。

这段代码展示了如何声明和使用全局和局部数组,并说明了它们的初始化差异。全局数组 a 被初始化为全零,而局部数组 b 的元素值是不确定的。

2) 二维数组

在实际使用中,还有一个非常重要的数组类型——二维数组,其结构如下图所示:


图 2 一个3行4列的二维数组

例如,以下代码用来创建一个二维数组,并输出数组的一个元素:
const int N = 1003;
const int M = 2003;
int a[N][M];                  // 创建一个1003 × 2003大小的二维数组

cout << a[1][2] << '\n';      // 输出数组a的第1行第2列的元素
上述代码首先定义了两个常量 N 和 M,分别赋值为 1003 和 2003。然后声明了一个名为 a 的二维数组,其大小为 1003 行 2003 列。最后,使用 cout 输出数组 a 的第 1 行第 2 列的元素,即 a[1][2]。

在算法竞赛中开数组时,我们一般会多开几个空间,例如题目要求 1000 大小,我们就可以开到 1003,1005,1007 等大小,这在编程时会提供一些便利。

3) 字符数组

关于数组,还有一种字符数组,即 C原生字符串,值得了解。字符数组实际上是一个 char 类型的数组,每个元素占据一个字节(Byte)的存储空间。

假如想存储一个长度为 n 的字符串,需要创建 n+1 大小的字符数组,如下图所示:


图 3 字符数组

因为字符数组的最后一位存储的是结束符'\0',它的出现意味着字符串到此为止。在 C/C++ 中,用单引号引起来的都是字符常量。字符数组有多种初始化方式,相比其他数组,它更为灵活。
char s[] = "Hello"; // 字符串长度为5,但字符数组长度为6

// 表示将字符串输入str[1]开始的字符数组中
// 注意此时需要创建n+2大小的字符数组,因为第0位没有被使用
char str[N];
cin >> str + 1;
上述代码将输入的字符串存储到一个字符数组中,但从数组的第 1 个位置(索引为 1)开始存储,而不是从第 0 个位置开始。这样做的目的是让数组的第0位保持空白,或用于其他目的。

首先,定义了一个字符数组 str,其长度为 N。然后,使用 cin >> str + 1;语句将输入的字符串存储到数组 str 中,从第 1 个位置开始。这里的 +1 表示将输入的字符串存储到数组的第 1 个位置,而不是第 0 个位置。

需要注意的是,为了确保有足够的空间存储输入的字符串以及可能的空字符(字符串结尾的 '\0'),数组 str 的大小应该至少为 N+2。这样,即使输入的字符串长度为 N-1,仍然可以在数组末尾添加一个空字符作为字符串结束标志。

C++数组初始化

当我们声明数组时,可以同时对其进行初始化。例如:
int arr[5] = {1, 2, 3, 4, 5};

如果只初始化数组的部分元素,未被初始化的元素将被赋予默认值(对于 int 类型,默认值为 0)。例如:
int arr[5] = {1, 2};      // 剩余的元素置为0
//arr = {1, 2, 0, 0, 0};

如果想要将所有元素都初始化为同一值,可以使用 std::fill 函数。例如:
int arr[5];
std::fill(arr, arr+5, 1);  // 将[arr, arr + 5]这段内存的值初始化为1

当然,最常用的方法是直接用 for 循环对数组进行初始化,这样最为灵活。例如:
int arr[5];
for (int i = 0; i < 5; ++i) arr[i] = 0;   // 将数组 arr 初始化为 0

int a[N][M];
// 初始化二维数组
// 将二维数组的 [1 ~ n][1 ~ m] 区域初始化为 0
for (int i = 1; i <= n; ++i)
    for (int j = 1; j <= m; ++j)
        a[i][j] = 0;

C++数组和指针的关系

在 C++ 中,数组名是一个指向数组第一个元素的指针。例如:
int arr[5] = {1, 2, 3, 4, 5};
int *p = arr;
此时,p 指向数组 arr 的第一个元素,我们可以通过解引用指针来访问元素:
cout << *p;           // 打印结果:1
cout << *(p+1);       // 打印结果:2

需要注意的是,尽管数组名可以作为指针使用,但数组名并不是一个真正的指针,它是一个常量指针,不能更改其值。例如,以下代码是错误的:
int arr[5], arr2[5];
arr = arr2;           // error: array type 'int [5]' is not assignable

在 C++ 中,指针的使用非常普遍,因为它们提供了一种高效的方式来处理内存和数据结构,尤其是在数组、字符串和动态分配的内存处理中。

例如,如果我们有一个整数数组,则可以创建一个指向数组第一个元素的指针,并通过增加指针值来访问数组中的其他元素:
int arr[] = {1, 2, 3, 4, 5};
int* p = arr;            // p指向数组的第一个元素

cout << *p;              // 输出1
cout << *(p+1);          // 输出2
cout << *(p+2);          // 输出3
// 以此类推
理解指针的概念对于成为一名熟练的 C++ 程序员至关重要,因为它们在许多高级编程技术和库中都发挥着核心作用。

相关文章