c++ 指针与数组

发布时间:2024年01月18日

指针和数组之间存在紧密的关系,数组名称实际上是指向其首个元素的指针,使得你能够使用指针操作来遍历和访问数组中的元素、传递数组到函数中等

数组

数组是多个相同类型元素的集合,它们在内存中连续存储。数组名代表整个数组,并可被视作指向数组首元素的指针。数组的大小在创建时就被固定,而且以后不能改变。
int myArray[10]; // 声明一个包含10个整数的数组
myArray[0] = 1;  // 访问数组第一个元素
// ...
myArray[9] = 10; // 访问数组最后一个元素

指针

指针是一个变量,它的值是另一个变量的地址。指针同样有类型,这个类型指明了指针指向的变量的类型。指针可以自由地改变并指向不同的地址。
int value = 5;
int* ptr = &value; // 指针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; // 数组名 arr 被用作指向首元素的指针

int arr[] = {1, 2, 3, 4, 5};
int* p = arr; // p 现在指向 arr 的第一个元素
int firstElement = *p; // 解引用得到arr[0]

指针遍历数组

通过递增指针来遍历数组的元素
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; // 使用形参中的指针访问数组元素;相当于 *(arr + i)
    }
}

void printArray(int* p, size_t size) {
    for (size_t i = 0; i < size; ++i) {
        std::cout << p[i] << ' '; // 访问数组元素
    }
}
printArray(arr, 5); // 调用函数时,arr退化成指向数组首元素的指针;传递数组,但函数接收的是指针

多维数组和指针

可以使用指针来指向多维数组
//p2D 指向 arr2D 中的第一行(也即第一个子数组)
int arr2D[2][3] = {{1, 2, 3}, {4, 5, 6}};
int (*p2D)[3] = arr2D; // p2D 是一个指向含有 3 个整数的数组的指针
for (int i = 0; i < 2; ++i) {
    for (int j = 0; j < 3; ++j) {
        std::cout << ptr[i][j] << ' '; // 访问元素
    }
    std::cout << std::endl;
}

数组与多级指针

当数组元素本身是指针时,你可能会遇见指向数组的指针的指针
//arr 是一个数组,其元素是指向 int 的指针。ptrToArr 是一个指向整个数组的指针
int* arr[3]; // 每个数组元素都是一个指向 int 的指针
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]; // 动态分配包含 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; // pConst 是一个指向常量整数的指针,不能通过pConst修改数组元素

注意事项

1、 使用未经初始化的指针可能会导致未定义行为。
2、超出数组范围的指针操作可能会导致数据破坏。
3、使用指针遍历数组时要确保不要超出数组边界。
4、动态分配的数组应使用 delete[] 来释放内存,以避免内存泄露。
文章来源:https://blog.csdn.net/weixin_43779276/article/details/135621210
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。