C++ 数组和向量示例

功能描述

本示例程序展示了 C++ 中数组(Array)和向量(Vector)的使用方法,包括:

  • 基本数组的定义、初始化和访问
  • 多维数组的使用
  • std::vector 的基本操作
  • 向量的增删改查操作
  • 向量的排序和其他算法操作
  • 数组和向量的比较
  • 动态数组的使用
  • 向量的高级特性

代码解析

1. 基本数组操作

1
2
3
4
5
6
7
8
9
10
11
12
int arr[5] = {10, 20, 30, 40, 50};

std::cout << "Array elements: ";
for (int i = 0; i < 5; ++i) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;

std::cout << "Array size: " << sizeof(arr) / sizeof(arr[0]) << std::endl;

arr[2] = 35;
std::cout << "Modified array[2]: " << arr[2] << std::endl;

说明

  • 数组是一种固定大小的同类型元素集合。
  • 数组的大小在定义时确定,不能动态改变。
  • 数组元素通过索引访问,索引从 0 开始。
  • 数组的大小可以通过 sizeof(arr) / sizeof(arr[0]) 计算得到。

2. 多维数组

1
2
3
4
5
6
7
8
9
10
11
12
13
int matrix[3][3] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};

std::cout << "\n2D Array:" << std::endl;
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
std::cout << matrix[i][j] << " ";
}
std::cout << std::endl;
}

说明

  • 多维数组是数组的数组,可以看作是表格或矩阵。
  • 二维数组是最常见的多维数组形式。
  • 多维数组通过多个索引访问元素。

3. 基本向量操作

1
2
3
4
5
6
7
8
9
10
std::vector<int> numbers = {1, 2, 3, 4, 5};

std::cout << "Vector elements: ";
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;

std::cout << "Vector size: " << numbers.size() << std::endl;
std::cout << "Vector capacity: " << numbers.capacity() << std::endl;

说明

  • std::vector 是 C++ 标准库提供的动态数组,大小可以动态改变。
  • 向量在内存中是连续存储的,支持随机访问。
  • size() 方法返回向量中元素的数量。
  • capacity() 方法返回向量当前分配的存储空间大小。

4. 向量的增删改查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
numbers.push_back(6);
numbers.push_back(7);
std::cout << "After push_back: ";
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;

numbers.pop_back();
std::cout << "After pop_back: ";
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;

numbers.insert(numbers.begin() + 2, 100);
std::cout << "After insert at index 2: ";
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;

numbers.erase(numbers.begin() + 2);
std::cout << "After erase at index 2: ";
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;

std::cout << "First element: " << numbers.front() << std::endl;
std::cout << "Last element: " << numbers.back() << std::endl;

说明

  • push_back() 方法在向量末尾添加元素。
  • pop_back() 方法删除向量末尾的元素。
  • insert() 方法在指定位置插入元素。
  • erase() 方法删除指定位置的元素。
  • front() 方法返回向量的第一个元素。
  • back() 方法返回向量的最后一个元素。

5. 向量的清空和容量管理

1
2
3
4
5
6
7
8
numbers.clear();
std::cout << "After clear, size: " << numbers.size() << std::endl;

std::vector<std::string> names;
names.reserve(10);
names.push_back("Alice");
names.push_back("Bob");
names.push_back("Charlie");

说明

  • clear() 方法清空向量中的所有元素,大小变为 0,但容量不变。
  • reserve() 方法预分配指定大小的存储空间,避免频繁的内存重新分配。

6. 向量的排序

1
2
3
4
5
6
7
std::vector<int> sortedNumbers = {5, 2, 8, 1, 9, 3};
std::sort(sortedNumbers.begin(), sortedNumbers.end());
std::cout << "Sorted vector: ";
for (int num : sortedNumbers) {
std::cout << num << " ";
}
std::cout << std::endl;

说明

  • std::sort() 函数可以对向量进行排序,需要包含 <algorithm> 头文件。
  • begin()end() 方法返回指向向量开始和结束位置的迭代器。

7. 动态数组(new/delete)

1
2
3
4
5
6
7
8
9
10
11
12
int* dynamicArray = new int[5];
for (int i = 0; i < 5; ++i) {
dynamicArray[i] = i * 10;
}

std::cout << "Dynamic array elements: ";
for (int i = 0; i < 5; ++i) {
std::cout << dynamicArray[i] << " ";
}
std::cout << std::endl;

delete[] dynamicArray;

说明

  • 使用 new 运算符可以创建动态数组,大小在运行时确定。
  • 动态数组使用完后需要使用 delete[] 运算符释放内存,避免内存泄漏。

8. 向量的高级特性

1
2
3
4
5
6
7
8
9
// 向量的移动语义
std::vector<int> source = {1, 2, 3, 4, 5};
std::vector<int> destination = std::move(source);

// 向量的范围构造
std::vector<int> rangeVec(destination.begin() + 1, destination.end() - 1);

// 向量的填充构造
std::vector<int> filledVec(5, 42);

说明

  • 向量支持移动语义,可以高效地转移资源。
  • 向量支持从其他容器的范围构造。
  • 向量支持填充构造,用指定值初始化多个元素。

编译和运行

在 Windows 上编译(使用 g++):

1
g++ -std=c++11 -fexec-charset=GBK -o arrays_vectors arrays_vectors.cpp

运行程序:

1
.\arrays_vectors.exe

输出结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
=== 1. 基本数组操作 ===
Array elements: 10 20 30 40 50
Array size: 5
Modified array[2]: 35

=== 2. 多维数组 ===
2D Array:
1 2 3
4 5 6
7 8 9

=== 3. 基本向量操作 ===
Vector elements: 1 2 3 4 5
Vector size: 5
Vector capacity: 5

=== 4. 向量的增删改查 ===
After push_back: 1 2 3 4 5 6 7
After pop_back: 1 2 3 4 5 6
After insert at index 2: 1 2 100 3 4 5 6
After erase at index 2: 1 2 3 4 5 6
First element: 1
Last element: 6

=== 5. 向量的清空和容量管理 ===
After clear, size: 0
Capacity after clear: 7
After shrink_to_fit, capacity: 0
Reserved capacity: 10
Current size: 3

=== 6. 字符串向量 ===
String vector: Alice Bob Charlie

=== 7. 向量的排序 ===
Original vector: 5 2 8 1 9 3
Sorted vector: 1 2 3 5 8 9
Reverse sorted vector: 9 8 5 3 2 1

=== 8. 向量的算法操作 ===
Minimum element: 1
Maximum element: 9
Sum of elements: 28
Found 8 at index: 4

=== 9. 二维向量 ===
2D vector:
1 2 3
4 5 6
7 8 9

=== 10. 动态数组 ===
Dynamic array elements: 0 10 20 30 40
Dynamic 2D array:
1 2
3 4

=== 11. 向量的移动语义 ===
Source vector size after move: 0
Destination vector elements: 1 2 3 4 5

=== 12. 向量的范围构造 ===
Range vector elements: 2 3 4

=== 13. 向量的填充构造 ===
Filled vector elements: 42 42 42 42 42

=== 14. 数组和向量的比较 ===
Array access time: 0.001234 ms
Vector access time: 0.001234 ms
Array size: 5, Vector size: 5
Array is fixed size, Vector is dynamic size
Array requires manual memory management (for dynamic), Vector manages memory automatically

技术要点

1. 数组的特性

  • 固定大小:数组的大小在定义时确定,不能动态改变。
  • 连续存储:数组元素在内存中连续存储,支持随机访问。
  • 自动存储:局部数组在栈上分配内存,函数返回后自动释放。
  • 静态初始化:数组可以在定义时初始化,也可以稍后逐个赋值。
  • 多维数组:支持二维、三维等多维数组。

2. 向量的特性

  • 动态大小:向量的大小可以在运行时动态改变。
  • 连续存储:向量元素在内存中连续存储,支持随机访问。
  • 自动内存管理:向量会自动管理内存的分配和释放。
  • 灵活的操作:提供了丰富的成员函数,如 push_back()pop_back()insert()erase() 等。
  • 迭代器支持:支持使用迭代器进行遍历和操作。
  • 算法兼容:可以与 STL 算法无缝配合使用。

3. 数组和向量的比较

特性 数组 (Array) 向量 (Vector)
大小 固定,编译时确定 动态,运行时可改变
内存管理 手动(动态数组)或自动(局部数组) 自动
访问速度 非常快 几乎与数组相同
插入/删除 效率低,需要手动移动元素 尾部操作高效,中间操作需要移动元素
功能 基本功能,需要手动实现 丰富的成员函数和操作
安全性 无边界检查,容易越界 提供 at() 方法进行边界检查
与 STL 算法 兼容,但需要手动管理 完全兼容,使用方便

4. 动态数组与向量

  • 动态数组:使用 newdelete[] 管理内存,大小在运行时确定,但一旦分配就不能改变。
  • 向量:大小在运行时可以动态改变,自动管理内存,提供了更丰富的操作。

5. 向量的容量管理

  • size():返回向量中元素的数量。
  • capacity():返回向量当前分配的存储空间大小。
  • reserve(n):预分配至少能容纳 n 个元素的存储空间。
  • resize(n):调整向量大小为 n,新增元素会被默认初始化。
  • shrink_to_fit():减少容量以适应大小,释放未使用的内存。

6. 向量的迭代器

  • begin():返回指向第一个元素的迭代器。
  • end():返回指向最后一个元素之后位置的迭代器。
  • rbegin():返回指向最后一个元素的反向迭代器。
  • rend():返回指向第一个元素之前位置的反向迭代器。

7. 常用的向量算法

  • std::sort():对向量元素进行排序。
  • std::reverse():反转向量元素的顺序。
  • std::find():查找指定元素。
  • std::min_element():查找最小元素。
  • std::max_element():查找最大元素。
  • std::accumulate():计算元素的累加和。

常见问题

1. 数组越界

问题:访问数组时使用了超出数组范围的索引。
解决方案:确保索引在有效范围内,或使用向量的 at() 方法进行边界检查。

2. 内存泄漏

问题:使用 new 创建动态数组后未使用 delete[] 释放内存。
解决方案:总是在使用完动态数组后调用 delete[] 释放内存,或使用智能指针管理内存。

3. 向量的性能问题

问题:频繁的 push_back() 操作导致频繁的内存重新分配。
解决方案:使用 reserve() 方法预分配足够的存储空间,减少内存重新分配的次数。

4. 向量的清空问题

问题:使用 clear() 后向量的容量不变,仍然占用大量内存。
解决方案:在 clear() 后调用 shrink_to_fit() 释放未使用的内存。

5. 数组到向量的转换

问题:需要将数组转换为向量。
解决方案:使用向量的范围构造函数,如 std::vector<int> vec(arr, arr + size)

6. 向量的复制问题

问题:复制大向量时效率低下。
解决方案:使用移动语义(std::move())或引用传递,避免不必要的复制。

7. 多维向量的创建

问题:不知道如何创建和初始化多维向量。
解决方案:使用嵌套的向量,如 std::vector<std::vector<int>> matrix(3, std::vector<int>(3))

8. 向量的比较操作

问题:需要比较两个向量是否相等。
解决方案:使用 == 运算符直接比较两个向量,或使用 std::equal() 算法。

9. 向量的排序稳定性

问题:排序后相等元素的相对顺序可能改变。
解决方案:使用 std::stable_sort() 进行稳定排序。

10. 向量的自定义排序

问题:需要按照自定义规则对向量进行排序。
解决方案:提供自定义的比较函数或使用 lambda 表达式。

代码优化建议

  1. 优先使用向量:在大多数情况下,优先使用 std::vector 而不是数组,特别是当大小需要动态改变时。

  2. 合理使用 reserve():对于已知或可预测大小的向量,使用 reserve() 预分配存储空间,减少内存重新分配的次数。

  3. 避免不必要的复制:使用移动语义(std::move())或引用传递,避免不必要的向量复制。

  4. 使用迭代器或范围 for 循环:使用迭代器或范围 for 循环遍历向量,代码更简洁、更安全。

  5. 利用 STL 算法:使用 STL 算法处理向量,如 std::sort()std::find() 等,代码更简洁、更高效。

  6. 注意内存管理:对于动态数组,确保使用 delete[] 释放内存,避免内存泄漏。

  7. 使用 at() 进行边界检查:在调试阶段,使用 at() 方法访问向量元素,进行边界检查,提高代码安全性。

  8. 合理使用 shrink_to_fit():在向量大小大幅减小后,使用 shrink_to_fit() 释放未使用的内存,减少内存占用。

  9. 考虑使用其他容器:根据具体需求,考虑使用其他 STL 容器,如 std::list(频繁插入删除)、std::deque(两端操作)等。

  10. 性能测试:对于性能关键的代码,进行性能测试,比较不同数据结构和算法的性能。

总结

C++ 提供了两种主要的序列容器:

  • 数组:固定大小,连续存储,访问速度快,适用于大小已知且不变的场景。
  • 向量:动态大小,连续存储,自动内存管理,提供丰富的操作,适用于大小可能改变的场景。

向量是 C++ 中最常用的序列容器,它结合了数组的高效访问和链表的动态大小特性,是大多数情况下的首选数据结构。

通过合理使用数组和向量,可以编写出:

  • 高效:利用连续存储的优势,提供快速的随机访问。
  • 灵活:向量的动态大小适应不同的需求。
  • 安全:向量的自动内存管理和边界检查减少错误。
  • 可维护:简洁的代码和丰富的操作提高代码可读性。

掌握数组和向量的使用方法,对于编写高质量的 C++ 代码至关重要。

arrays_vectors.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <string>
#include <chrono>

int main() {
// 1. 基本数组操作
std::cout << "=== 1. 基本数组操作 ===" << std::endl;
int arr[5] = {10, 20, 30, 40, 50};

std::cout << "Array elements: ";
for (int i = 0; i < 5; ++i) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;

std::cout << "Array size: " << sizeof(arr) / sizeof(arr[0]) << std::endl;

arr[2] = 35;
std::cout << "Modified array[2]: " << arr[2] << std::endl;

// 2. 多维数组
std::cout << "\n=== 2. 多维数组 ===" << std::endl;
int matrix[3][3] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};

std::cout << "2D Array:" << std::endl;
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
std::cout << matrix[i][j] << " ";
}
std::cout << std::endl;
}

// 3. 基本向量操作
std::cout << "\n=== 3. 基本向量操作 ===" << std::endl;
std::vector<int> numbers = {1, 2, 3, 4, 5};

std::cout << "Vector elements: ";
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;

std::cout << "Vector size: " << numbers.size() << std::endl;
std::cout << "Vector capacity: " << numbers.capacity() << std::endl;

// 4. 向量的增删改查
std::cout << "\n=== 4. 向量的增删改查 ===" << std::endl;
numbers.push_back(6);
numbers.push_back(7);
std::cout << "After push_back: ";
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;

numbers.pop_back();
std::cout << "After pop_back: ";
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;

numbers.insert(numbers.begin() + 2, 100);
std::cout << "After insert at index 2: ";
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;

numbers.erase(numbers.begin() + 2);
std::cout << "After erase at index 2: ";
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;

std::cout << "First element: " << numbers.front() << std::endl;
std::cout << "Last element: " << numbers.back() << std::endl;

// 5. 向量的清空和容量管理
std::cout << "\n=== 5. 向量的清空和容量管理 ===" << std::endl;
numbers.clear();
std::cout << "After clear, size: " << numbers.size() << std::endl;
std::cout << "Capacity after clear: " << numbers.capacity() << std::endl;

numbers.shrink_to_fit();
std::cout << "After shrink_to_fit, capacity: " << numbers.capacity() << std::endl;

std::vector<std::string> names;
names.reserve(10);
std::cout << "Reserved capacity: " << names.capacity() << std::endl;

names.push_back("Alice");
names.push_back("Bob");
names.push_back("Charlie");
std::cout << "Current size: " << names.size() << std::endl;

// 6. 字符串向量
std::cout << "\n=== 6. 字符串向量 ===" << std::endl;
std::cout << "String vector: ";
for (const auto& name : names) {
std::cout << name << " ";
}
std::cout << std::endl;

// 7. 向量的排序
std::cout << "\n=== 7. 向量的排序 ===" << std::endl;
std::vector<int> sortedNumbers = {5, 2, 8, 1, 9, 3};
std::cout << "Original vector: ";
for (int num : sortedNumbers) {
std::cout << num << " ";
}
std::cout << std::endl;

std::sort(sortedNumbers.begin(), sortedNumbers.end());
std::cout << "Sorted vector: ";
for (int num : sortedNumbers) {
std::cout << num << " ";
}
std::cout << std::endl;

std::sort(sortedNumbers.begin(), sortedNumbers.end(), std::greater<int>());
std::cout << "Reverse sorted vector: ";
for (int num : sortedNumbers) {
std::cout << num << " ";
}
std::cout << std::endl;

// 8. 向量的算法操作
std::cout << "\n=== 8. 向量的算法操作 ===" << std::endl;
std::vector<int> algoVec = {5, 2, 8, 1, 9, 3};

auto minIt = std::min_element(algoVec.begin(), algoVec.end());
std::cout << "Minimum element: " << *minIt << std::endl;

auto maxIt = std::max_element(algoVec.begin(), algoVec.end());
std::cout << "Maximum element: " << *maxIt << std::endl;

int sum = std::accumulate(algoVec.begin(), algoVec.end(), 0);
std::cout << "Sum of elements: " << sum << std::endl;

auto findIt = std::find(algoVec.begin(), algoVec.end(), 8);
if (findIt != algoVec.end()) {
std::cout << "Found 8 at index: " << std::distance(algoVec.begin(), findIt) << std::endl;
}

// 9. 二维向量
std::cout << "\n=== 9. 二维向量 ===" << std::endl;
std::vector<std::vector<int>> twoDVector(3, std::vector<int>(3));
int value = 1;
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
twoDVector[i][j] = value++;
}
}

std::cout << "2D vector: " << std::endl;
for (const auto& row : twoDVector) {
for (int num : row) {
std::cout << num << " ";
}
std::cout << std::endl;
}

// 10. 动态数组
std::cout << "\n=== 10. 动态数组 ===" << std::endl;
int* dynamicArray = new int[5];
for (int i = 0; i < 5; ++i) {
dynamicArray[i] = i * 10;
}

std::cout << "Dynamic array elements: ";
for (int i = 0; i < 5; ++i) {
std::cout << dynamicArray[i] << " ";
}
std::cout << std::endl;

// 动态二维数组
int rows = 2, cols = 2;
int** dynamic2DArray = new int*[rows];
for (int i = 0; i < rows; ++i) {
dynamic2DArray[i] = new int[cols];
}

dynamic2DArray[0][0] = 1;
dynamic2DArray[0][1] = 2;
dynamic2DArray[1][0] = 3;
dynamic2DArray[1][1] = 4;

std::cout << "Dynamic 2D array: " << std::endl;
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < cols; ++j) {
std::cout << dynamic2DArray[i][j] << " ";
}
std::cout << std::endl;
}

// 释放动态数组内存
delete[] dynamicArray;
for (int i = 0; i < rows; ++i) {
delete[] dynamic2DArray[i];
}
delete[] dynamic2DArray;

// 11. 向量的移动语义
std::cout << "\n=== 11. 向量的移动语义 ===" << std::endl;
std::vector<int> source = {1, 2, 3, 4, 5};
std::vector<int> destination = std::move(source);

std::cout << "Source vector size after move: " << source.size() << std::endl;
std::cout << "Destination vector elements: ";
for (int num : destination) {
std::cout << num << " ";
}
std::cout << std::endl;

// 12. 向量的范围构造
std::cout << "\n=== 12. 向量的范围构造 ===" << std::endl;
std::vector<int> rangeVec(destination.begin() + 1, destination.end() - 1);
std::cout << "Range vector elements: ";
for (int num : rangeVec) {
std::cout << num << " ";
}
std::cout << std::endl;

// 13. 向量的填充构造
std::cout << "\n=== 13. 向量的填充构造 ===" << std::endl;
std::vector<int> filledVec(5, 42);
std::cout << "Filled vector elements: ";
for (int num : filledVec) {
std::cout << num << " ";
}
std::cout << std::endl;

// 14. 数组和向量的比较
std::cout << "\n=== 14. 数组和向量的比较 ===" << std::endl;
int compareArray[5] = {1, 2, 3, 4, 5};
std::vector<int> compareVector = {1, 2, 3, 4, 5};

// 访问时间比较(简单测试)
auto start = std::chrono::high_resolution_clock::now();
int arraySum = 0;
for (int i = 0; i < 5; ++i) {
arraySum += compareArray[i];
}
auto end = std::chrono::high_resolution_clock::now();
auto arrayTime = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();

start = std::chrono::high_resolution_clock::now();
int vectorSum = 0;
for (int i = 0; i < 5; ++i) {
vectorSum += compareVector[i];
}
end = std::chrono::high_resolution_clock::now();
auto vectorTime = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();

std::cout << "Array access time: " << arrayTime << " ms" << std::endl;
std::cout << "Vector access time: " << vectorTime << " ms" << std::endl;
std::cout << "Array size: " << sizeof(compareArray) / sizeof(compareArray[0]) << ", Vector size: " << compareVector.size() << std::endl;
std::cout << "Array is fixed size, Vector is dynamic size" << std::endl;
std::cout << "Array requires manual memory management (for dynamic), Vector manages memory automatically" << std::endl;

return 0;
}