1. 访问数组元素
1.1 使用下标运算符
数组的基本声明形式为:
type arrayName[dimension];
其中,dimension
表示数组中元素的个数,下标从 0 开始,因此对于一个含有 10 个元素的数组,其合法下标范围是 0 到 9。
例如:
#include <iostream>
using std::cout;
using std::endl;
int main() {
int arr[10] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
// 访问第三个元素(下标为2)
cout << "arr[2] = " << arr[2] << endl;
return 0;
}
提示:
访问数组元素时,建议使用size_t
类型作为下标变量,因为size_t
是无符号类型且足够大来表示内存中任意对象的大小,定义在<cstddef>
中。
1.2 使用范围 for 语句遍历数组
范围 for 语句可以简化数组元素的遍历。编译器会自动推导数组的大小,因此无需手动控制循环变量。
示例:
#include <iostream>
using std::cout;
using std::endl;
int main() {
int scores[11] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 遍历数组中的所有元素并输出
for (auto count : scores)
cout << count << " ";
cout << endl;
return 0;
}
在上述代码中,范围 for 语句会遍历数组 scores
中的每个元素,并依次输出。
2. 指针与数组
2.1 数组名自动转换为指针
在 C++ 中,数组名在许多表达式中会自动转换为指向数组首元素的指针。例如:
#include <iostream>
#include <string>
using std::string;
using std::cout;
using std::endl;
int main() {
string nums[] = {
"one", "two", "three"};
// 显式地取数组首元素的地址
string *p = &nums[0];
// 数组名在表达式中自动转换为指向首元素的指针
string *p2 = nums; // 等价于 p2 = &nums[0]
cout << "First element: " << *p2 << endl; // 输出 "one"
return 0;
}
2.2 使用 auto 推导数组指针类型
当使用数组作为 auto 变量的初始值时,数组会自动转换为指向首元素的指针。例如:
#include <iostream>
using std::cout;
using std::endl;
int main() {
int ia[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
auto ia2 = ia; // ia2 的类型是 int*, 指向数组 ia 的首元素
//ia2 = 42; // 错误:不能将整型值赋给一个指针
cout << "ia2[2] = " << ia2[2] << endl; // 输出 2
return 0;
}
注意:
如果使用decltype(ia)
来获取类型,则返回的是整个数组类型,而不是指针。例如:decltype(ia) ia3 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; // ia3 的类型为 int[10] // ia3 = ia2; // 错误:类型不匹配
2.3 指针运算与数组下标
指针可以执行与迭代器类似的随机访问运算。当指针指向数组中的某个元素时,可以利用加法运算符移动指针到其它位置,并使用解引用运算符获取对应元素。例如:
#include <iostream>
using std::cout;
using std::endl;
int main() {
int arr[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int *p = arr; // p 指向 arr[0]
int *e = arr + 10; // e 指向 arr 尾元素的下一位置
// 使用递增运算遍历数组
for (int *it = p; it != e; ++it)
cout << *it << " ";
cout << endl;
// 指针运算示例:获取 arr[4] 的值
int last = *(arr + 4); // 等价于 arr[4]
cout << "arr[4] = " << last << endl;
return 0;
}
提示:
对于数组下标运算arr[n]
,编译器实际上将其转换为*(arr + n)
。因此,只要确保arr + n
在合法范围内,就可以通过解引用获取对应元素。
2.4 使用标准库函数 begin 和 end
C++11 引入了 begin()
和 end()
函数,用于获取数组首元素和尾后位置的指针,使得遍历数组更加安全和方便。示例:
#include <iostream>
#include <iterator> // 定义了 begin 和 end
using std::cout;
using std::endl;
int main() {
int ia[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int *beg = std::begin(ia); // 指向 ia 的首元素
int *last = std::end(ia); // 指向 ia 尾元素的下一位置
// 遍历数组
for (int *p = beg; p != last; ++p)
cout << *p << " ";
cout << endl;
return 0;
}
这样,无论数组大小如何,使用 std::begin()
和 std::end()
都可以确保遍历在合法范围内进行。
3. 总结
-
数组名自动转换:
在大多数表达式中,数组名自动转换为指向其首元素的指针,使得很多数组操作实际上是指针操作。 -
指针与下标:
通过下标运算符访问数组元素时,编译器将数组名转换为指针,然后执行指针加法和解引用。 -
指针运算:
指针支持递增、加减运算以及相减得到距离,这与迭代器运算非常相似。确保运算结果指向同一数组内的有效位置(或尾后位置)。 -
标准库 begin/end:
C++11 提供的std::begin()
和std::end()
函数使得获取数组首元素和尾后位置的指针更安全、易用。 -
类型推导与数组:
使用 auto 定义变量时,如果初始值是数组,则会转换为指向首元素的指针,而使用 decltype 则可以获取整个数组类型。
通过深入理解数组与指针的关系,以及如何利用标准库函数安全地访问数组元素,你可以编写出既高效又安全的 C++ 代码,避免因数组越界而产生的安全问题。
参考资料
- cppreference.com 关于数组、指针以及 std::begin()/std::end() 的详细说明
- 各大 C++ 编码规范(如 Google C++ Style Guide)中对数组访问的建议
希望这篇详细讲解能帮助你全面理解数组和指针的关系,从而更安全有效地访问和操作内置数组中的元素。