在C++编程中,数组和指针是两个紧密关联但又存在本质区别的概念,其中数组名作为指针常量的特性是很多开发者容易混淆的知识点。理解两者的关系,能够帮助我们在处理内存访问、函数传参等场景时避免错误。
数组名作为指针常量的本质
在大多数表达式中,数组名会被隐式转换为指向数组首元素的指针,这个指针的类型是元素类型*,并且它是一个指针常量,也就是说指针本身的值不能被修改,不能对数组名进行赋值操作。
我们可以通过下面的代码来验证这个特性:
#include <iostream>
using namespace std;
int main() {
int arr[5] = {1, 2, 3, 4, 5};
// arr 是 int* const 类型的指针常量,指向数组首元素
int* p = arr; // 数组名隐式转换为指向首元素的指针,赋值给普通指针p
cout << "arr的值(首元素地址): " << arr << endl;
cout << "p的值: " << p << endl;
cout << "arr[0]的值: " << arr[0] << endl;
cout << "*p的值: " << *p << endl;
// arr = p; // 错误:数组名是指针常量,不能被赋值
return 0;
}
数组名和普通指针的差异
虽然数组名在很多场景下表现得像指针,但它和普通指针存在明显区别,最核心的差异在于sizeof运算的结果不同。
对数组名使用sizeof,得到的是整个数组占用的总字节数;而对普通指针使用sizeof,得到的是指针本身的大小,在64位系统中通常是8字节,32位系统中是4字节。
#include <iostream>
using namespace std;
int main() {
int arr[5] = {1, 2, 3, 4, 5};
int* p = arr;
cout << "sizeof(arr): " << sizeof(arr) << endl; // 输出20,5个int每个4字节
cout << "sizeof(p): " << sizeof(p) << endl; // 64位系统输出8
return 0;
}
另外,对数组名取地址得到的结果,和对普通指针取地址的结果也不同。对数组名取地址,得到的是指向整个数组的指针,类型为元素类型(*)[数组长度];而对普通指针取地址,得到的是指针变量的地址。
#include <iostream>
using namespace std;
int main() {
int arr[5] = {1, 2, 3, 4, 5};
int* p = arr;
// 对数组名取地址,得到指向整个数组的指针
int (*parr)[5] = &arr;
cout << "arr的地址: " << arr << endl;
cout << "&arr的地址: " << &arr << endl;
cout << "p的地址: " << p << endl;
cout << "&p的地址: " << &p << endl;
// 虽然arr和&arr的值相同,但类型不同,运算时的步长不同
cout << "arr + 1的地址: " << arr + 1 << endl; // 步长为4字节,指向第二个元素
cout << "&arr + 1的地址: " << &arr + 1 << endl; // 步长为20字节,指向数组末尾之后
return 0;
}
数组名作为指针常量的常见使用场景
函数传参
当数组作为函数参数传递时,数组名会退化为指向首元素的指针,函数内部无法通过sizeof获取数组的长度,因此需要额外传递数组长度参数。
#include <iostream>
using namespace std;
// 数组作为参数时,等价于int* arr,无法通过sizeof获取数组长度
void printArr(int arr[], int len) {
for (int i = 0; i < len; i++) {
cout << arr[i] << " ";
}
cout << endl;
}
int main() {
int arr[5] = {1, 2, 3, 4, 5};
printArr(arr, 5);
return 0;
}
指针运算
数组名作为指针常量,可以参与指针运算,通过偏移来访问数组的不同元素,这也是数组下标访问的本质,arr[i]等价于*(arr + i)。
#include <iostream>
using namespace std;
int main() {
int arr[5] = {1, 2, 3, 4, 5};
// 通过指针运算访问数组元素
for (int i = 0; i < 5; i++) {
cout << *(arr + i) << " ";
}
cout << endl;
return 0;
}
注意事项
- 不要尝试修改数组名的值,因为它是指针常量,赋值操作会导致编译错误。
- 不要混淆数组名和指向数组的指针,两者的类型不同,参与运算时的步长也不同。
- 当数组作为函数参数时,要额外传递数组长度,避免函数内部访问越界。
总结来说,C++中数组名在大多数场景下会作为指向首元素的指针常量使用,它和普通指针的核心差异在于sizeof运算结果和取地址后的类型,理解这些特性能够帮助我们更准确地使用数组和指针,减少代码错误。