指针和数组之间存在紧密的关系,数组名称实际上是指向其首个元素的指针,使得你能够使用指针操作来遍历和访问数组中的元素、传递数组到函数中等
数组
数组是多个相同类型元素的集合,它们在内存中连续存储。数组名代表整个数组,并可被视作指向数组首元素的指针。数组的大小在创建时就被固定,而且以后不能改变。
int myArray[10];
myArray[0] = 1;
myArray[9] = 10;
指针
指针是一个变量,它的值是另一个变量的地址。指针同样有类型,这个类型指明了指针指向的变量的类型。指针可以自由地改变并指向不同的地址。
int value = 5;
int* ptr = &value;
*ptr = 10;
指针和数组的关系
数组名单独使用时几乎总是被转换为指向其首元素的指针。意味着可以通过指针来访问和遍历数组。
int myArray[10];
int* ptrToArray = myArray;
关键差异
1、大小:使用 sizeof 运算符,数组会给出整个数组的大小,而指针只会给出指针本身的大小。
2、类型:数组类型包含了它的长度信息(例如 int[5] 是一个类型),而指针类型不包括任何长度信息(例如 int*)。
3、可变性:数组名总是指向数组的第一个元素,它本身不是一个可变的值,起始地址是不变的;
而指针的值是可变的,可以指向数组中的任意一个元素或者完全不同的内存区域。可以被重新赋值以指向另一个地址。
4、自增: 对数组进行自增操作是非法的,但指针可以进行自增操作来遍历它所指向的内存块。
5、参数传递: 在函数参数传递中,数组总是以指针的形式传递,而指针可以作为其本身传递。传递数组时,通常需要一个额外的参数来指示数组的大小,因为只传递指针时不包含大小信息。
数组名作为指针
当使用数组名称时,它通常会被解释为指向数组第一个元素的指针
int arr[3] = {10, 20, 30};
int* ptr = arr;
int arr[] = {1, 2, 3, 4, 5};
int* p = arr;
int firstElement = *p;
指针遍历数组
通过递增指针来遍历数组的元素
for (int i = 0; i < 5; ++i) {
std::cout << *(p + i) << ' ';
}
指针运算和数组下标
指针运算和数组下标基本上是等价的
需要注意的是:指针运算必须保持在数组边界之内。越界操作可能导致不可预料的行为,包括程序崩溃或数据损坏。
std::cout << p[2] << std::endl;
std::cout << *(p + 2) << std::endl;
函数和数组参数
在函数参数中,数组被看作指针。因此,传递数组参数实际上是传递指向数组首元素的指针。
另请注意,函数无法判断数组的大小,因此通常需要传递数组的大小作为另一个参数。
void printArray(const int* arr, int size) {
for (int i = 0; i < size; ++i) {
cout << arr[i] << endl;
}
}
void printArray(int* p, size_t size) {
for (size_t i = 0; i < size; ++i) {
std::cout << p[i] << ' ';
}
}
printArray(arr, 5);
多维数组和指针
可以使用指针来指向多维数组
int arr2D[2][3] = {{1, 2, 3}, {4, 5, 6}};
int (*p2D)[3] = arr2D;
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < 3; ++j) {
std::cout << ptr[i][j] << ' ';
}
std::cout << std::endl;
}
数组与多级指针
当数组元素本身是指针时,你可能会遇见指向数组的指针的指针
int* arr[3];
int** ptrToArr = arr;
指针与动态分配的数组(堆分配)
指针也被用来访问动态分配的数组,这些数组使用 new 关键字在堆上分配
int* dynamicArray = new int[3];
dynamicArray[0] = 10;
dynamicArray[1] = 20;
dynamicArray[2] = 30;
delete[] dynamicArray;
int* dynamicArray = new int[5];
for (int i = 0; i < 5; ++i) {
dynamicArray[i] = i * i;
}
delete[] dynamicArray;
指针常量和数组
数组到指针的转换不会改变数组本身的常量性质。如果你有一个常量数组,它会转化为指向常量的指针
const int arrConst[] = {1, 2, 3, 4, 5};
const int* pConst = arrConst;
注意事项
1、 使用未经初始化的指针可能会导致未定义行为。
2、超出数组范围的指针操作可能会导致数据破坏。
3、使用指针遍历数组时要确保不要超出数组边界。
4、动态分配的数组应使用 delete[] 来释放内存,以避免内存泄露。