前言:
前面我们学习了标准库类型vector,那么在这里我们对数组加以了解。我认为,数组更像是操作不灵活的vector容器,我们定义数组的时候会定义其大小,在这个程序中该数组自定义后则不可改变,所以比起vector,数组稳定性相对较强,灵活性相对较弱。
定义和初始化数组:
1.定义
前面我们说过数组的大小是确定的,换言之定义数组的维度和大小时应当使用常量表达式。那么在这里我们尝试定义一个大小为10的数组并实现单数为1双数为2的初始化。(ep5.1)
//ep5.1
#include<iostream>
using namespace std;
int main() {
int arr[10]; // 定义一个大小为10的数组
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) // 单数为1
arr[i] = 1;
else arr[i] = 2; // 双数为2
}
for (auto i : arr) {
cout << i << " ";
}
return 0;
}
在这里可以看到定义数组arr时其大小是确定的,换言之,此为常量表达式的一种形式。那么再看以下定义方式:
unsigned arr = 4; // 错误,不是常量表达式
constexpr unsigned ss = 4; // constexpr强约束,仅对于指针有效
int *p[ss]; // 正确,ss已定义为常量表达式,含有4个整型指针的数组
需要注意的是数组在定义的时候就要规定好它的类型。
2.初始化
Ⅰ.显式初始化
在前面定义环节我们已经接触了用for循环初始化。那么于vector相似的,数组也有显式初始化
int arr1 = {0, 1, 2, 3}; // 一个维度为4的数组arr1
int arr2[4] = {1}; // arr2其实是{1, 0, 0, 0}
string arr3[4] = {"2024", "大暑"}; // arr3内容为{"2024", "大暑", "", ""}
int arr4[3] = {0, 1, 2, 3}; // 报错,超出了范围限制
Ⅱ.数组的拷贝赋值
数组中不能将一个数组直接拷贝给另一个数组作为初始值,例如这样(ep5.2):
//ep5.2
#include<iostream>
using namespace std;
int main() {
int arr1[] = {0, 1, 2, 3, 4};
int arr2[5];
arr2 = arr1;
for (auto i : arr2) {
cout << i << " ";
}
return 0;
}
显而易见的报错。那么我们该如何进行数组内容拷贝呢,在这里我们提一下STL中的copy算法(ep5.3)。
std::copy(std::begin(数组1), std::end(数组1), std::begin(数组2))
意为:原数组为数组1,需要将数组1中的内容拷贝到数组2中。
//ep5.3
#include<iostream>
using namespace std;
int main() {
int arr1[] = {0, 1, 2, 3, 4};
int arr2[5];
copy(begin(arr1), end(arr1), begin(arr2));
for (auto i : arr2) {
cout << i << " ";
}
return 0;
}
Ⅲ.string类型的拷贝
特别地,我们在这里提一下拷贝赋值问题,以上报错案例(ep5.2中)我们说数组不能直接拷贝赋值,但在string中可以进行这类运算(ep5.4)。
//ep5.4
//string类型的浅拷贝
#include<iostream>
#include<cstring>
using namespace std;
int main() {
string a("Hello");
string b("World!");
cout << a + " " << b << endl;
b = a;
cout << a + " " << b << endl;
return 0;
}
ep5.4的输出为:
Hello World!
Hello Hello
在这里我们要了解一下浅拷贝与深拷贝。在ep5.4中我们定义了两个string类型的值a和b,并尝试将a的值赋给b,那么在这里的b=a操作,实际上是将b的指针指向了a,使原本a和b的两个指针同时指向a而无指针指向b,那么这样就会导致改变a的值的同时b的值也被动改变而b的值无法单独改变。但是目前的C++11已经无须我们手动分辨浅拷贝与深拷贝。可以用以下程序进行试验(ep5.5)。
//ep5.5
#include<iostream>
#include<cstring>
using namespace std;
int main() {
string a("Hello");
string b(a);
cout << a + " " << b << endl;
a = "World!";
cout << a + " " << b << endl;
return 0;
在以上的ep5.4和ep5.5中,我们发现无论是传统意义上的浅拷贝还是深拷贝都会被系统归为深拷贝。至于要在这里提一下呢,是因为我突然想到了。。。。。。
访问数组的值
1.for循环+auto
2.for循环+下标
访问数组值,最普遍的是用for循环+auto关键字或下标进行(ep5.6)。
//ep5.6
#include <iostream>
using namespace std;
int main() {
// 定义一个大小为10的数组arr,并初始化为0到9
int arr[10];
for(int i = 0; i < 10; i++) {
arr[i] = i;
}
// 遍历数组,对每个元素应用条件判断
for(int i = 0; i < 10; i++) {
if(arr[i] >= 5) {
arr[i] -= 1; // 当数值大于等于5时,值减1
} else {
arr[i] += 1; // 否则,值加1
}
}
// 输出改变数值后的数组
cout << "修改后的数组为:" << endl;
for (auto i : arr) {
cout << i << " ";
}
cout << endl;
return 0;
}
多维数组
前面我们学过vector中的多维数组,也就是类似于vector<vector<int>> vec。相似的,数组也可以这样,例如定义一个二维数组:
//ep5.7
// 二维数组
//方式一:每行加花括号
#include<iostream>
using namespace std;
int main() {
int a1[4][4] = {
{0, 1, 2, 3},
{1, 2, 3, 4},
{2, 3, 4, 5},
{3, 4, 5, 6}
};
cout << "数组a1的内容为:" << endl;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
cout << a1[i][j] << " ";
}
}
cout << endl;
//方式二:直接定义
int a2[4][4] = {0, 1, 2, 3, 1, 2, 3, 4,2, 3, 4, 5, 3, 4, 5, 6};
cout << "数组a2的内容为:" << endl;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
cout << a2[i][j] << " ";
}
}
cout << endl;
//方式三:初始化每行的首元素
// for + auto 关键字:
int a3[4][4] = {{ 1 }, { 2 }, { 3 }, { 4 }};
cout << "数组a3的内容为:" << endl;
for (const auto &i : a3) { // 行
for (auto j : i) { // 列
cout << j << " ";
}
}
cout << endl;
return 0;
}
类似的三维数组多维数组也相似,大家可以自己尝试。最后需要注意的是for+auto关键字遍历内容的行列限定,可参考ep5.7数组a3的遍历方式。