函数通过指针对多维数组进行处理
int arr[3][4];
1)数组名arr同时也是数组首元素的地址
2)arr是数组首元素的地址,arr的值和&arr[0]相同;arr[0]本身是包含4个整数的数组,arr[0]的值同其首元素的地址&arr[0][0]相同;另个整数组成的数组开始于同一地址,arr和arr[0]具有相同的数值
3)对一个指针(即地址)加1,会对原来的数值加上一个对应类型大小的数值;arr所指向的对象的大小是2个int,arr[0]所指向的对象的大小是1个int,因此arr + 1和arr[0] + 1的结果不同
4)对一个指针(即地址)取值(使用运算符*或带索引的[]运算符)得到的是该指针所指向的对象的数值;arr[0]是其首元素arr[0][0]的地址,所以*(arr[0])代表存储在arr[0][0]中的数值,即一个int数值;*arr代表其首元素arr[0]的值,但是arr[0]本身就是一个int数的地址,即&arr[0][0],因此*arr是&arr[0][0],因此**arr等价于*&arr[0][0],即等价于arr[0][0];arr是地址的地址,需要两次取值(**arr)才可以得到通常的数值
地址的地址(或指针的指针)是双重间接的典型例子
示例代码:
#include <stdio.h>
int main(void)
{
int arr[3][4] = {
{1, 2, 3, 4 },
{5, 6, 7, 8 },
{9, 10, 11, 12}
};
printf("arr = %p, arr + 1 = %p\n", arr, arr + 1);
printf("arr[0] = %p, arr[0] + 1 = %p\n", arr[0], arr[0] + 1);
printf("*arr = %p, *arr + 1 = %p\n", *arr, *arr + 1);
printf("arr[0][0] = %d\n", arr[0][0]);
printf("*arr[0] = %d\n", *arr[0]);
printf("**arr = %d\n", **arr);
printf("arr[2][1] = %d\n", arr[2][1]);
printf("*(*(arr + 2) + 1) = %d\n", *(*(arr + 2) + 1));
}
运行结果:
一、如何声明指向二维数组的指针变量
int main(void)
{
int arr[3][4] = {
{1, 2, 3, 4 },
{5, 6, 7, 8 },
{9, 10, 11, 12}
};
int (* p) [4]; // p是指向包含4个int值的数组的指针
p = arr;
...
}
尽管p是一个指针,不是数组名,依然可以使用p[2][1]这样的符号
二、指针的兼容性
指针之间的赋值规则比数值类型的赋值更加严格
例如,对于数值类型,可以不需要进行类型转换就直接把一个int数值赋给一个double类型的变量,但对于指针来说这种赋值是不被允许的
三、函数和多维数组
如果要编写处理二维数组的函数,需要很好地理解指针以便正确声明函数的类型
在函数体内,通常可以用数组符号来避免使用指针