从数组变量开始
我们都知道,使用int array[3] = {4,5,6};
可以定义一个名为array
的数组变量。
这时我们需要了解一个点:
array
代表了{4,5,6}这个数组的第一个数的地址(首地址)
接下来,我们可能想直接操作array
比如auto coumt = array[1];
实际上是先用array获取数组起始地址,然后加上i * sizeof(int)
的偏移量访问对应的元素,但并不相等,[]
操作符会解引用得到真实的值。
关于数组访问还有一个知识点:
数组访问越界(比如array[-1]或array[5])通常会编译器报错,但不会导致真正的内存访问错误。
只是编译器出于保护的考虑,不会让数组索引越界访问。但理论上在内存中,数组越界不会导致真正的访问违例。应严格遵守数组边界
综上:我们有了一个数组
int array[3] = {4,5,6};
1.指向整个数组的指针
因为[]
操作符的优先级
[]操作符具有很高的优先级,他会先和ptr
结合,导致int
只能和*
结合,最后变成了(int*) ptr = &array
(见:)
因此我们需要使用括号,让语句变成 int []的ptr
int (*ptr)[] = &array;
[]
会和int
结合,再表明ptr
代表了int[]
类型的指针。
操作指向整个数组的指针
获取到了指向整个数组的指针,其实使用起来没有多少异样。
int x = (*ptr)[0]; // 获取数组的第一个元素
int y = (*ptr)[1]; // 获取数组的第二个元素
int z = (*ptr)[2]; // 获取数组的第三个元素
(*ptr)[0] = 10; // 将数组的第一个元素设置为10
(*ptr)[1] = 20; // 将数组的第二个元素设置为20
(*ptr)[2] = 30; // 将数组的第三个元素设置为30
但依然有一些很实用的场景:
数组指针作为返回值
int (*get_array())[3] {
static int array[3] = {1, 2, 3};
return &array;
}
// 调用函数并获取返回的指针
int (*ptr2)[3] = get_array();
printf("%d %d %d\n", (*ptr2)[0], (*ptr2)[1], (*ptr2)[2]); // 输出:1 2 3
数组指针作为参数
void print_array(int (*ptr)[3]) {
for (int i = 0; i < 3; i++) {
printf("%d ", (*ptr)[i]);
}
printf("\n");
}
// 调用函数并传递指针作为参数
print_array(ptr); // 输出:1 2 3
2.元素皆为指针的数组
简单来说,不考虑优先级就是这个结果
int *ptr[]
可以这样创建一个元素皆为指针的数组
int a = 1, b = 2, c = 3;
int *ptr[] = {&a, &b, &c};
可以这样操作
int x = *(ptr[0]); // 获取第一个元素的值(即 a 的值)
*(ptr[1]) = 5; // 将第二个元素(即 b)的值设置为 5
3.指向「元素皆为指针的数组」的指针
指向「元素皆为指针的数组」的指针可以通过以下方式声明:
int *arr[3]; // 声明一个包含三个指向 int 类型指针的数组
int **ptr = arr; // 声明一个指向指针数组的指针
在这个例子中,arr
是一个包含三个指向 int
类型指针的数组。ptr
是一个指向指针数组的指针,它被初始化为 arr
的地址。因为 arr
是一个指针数组,所以 ptr
的类型是 int **
,即指向指针的指针。
可以通过以下方式访问和修改 ptr
所指向的指针数组中的元素:
int a = 1, b = 2, c = 3;
arr[0] = &a;
arr[1] = &b;
arr[2] = &c;
*arr[1] = 5; // 将第二个元素(即 b)所指向的变量的值设置为 5
在这个例子中,arr
数组的三个元素分别被赋值为 &a
、&b
和 &c
,即指向 a
、b
和 c
变量的指针。*arr[1] = 5
将 arr
数组的第二个元素(即 b
的指针)所指向的变量的值设置为 5。因为 ptr
指向 arr
,所以也可以通过 ptr
访问和修改 arr
数组中的元素:
*(*(ptr + 1)) = 5; // 将第二个元素(即 b)所指向的变量的值设置为 5
在这个例子中,*(ptr + 1)
获取了 arr
数组的第二个元素的地址,即 &b
,*(*(ptr + 1))
获取了 b
的值,并将其设置为 5。