数组指针:
int temp[5] = { 2, 3, 4, 5, 6 }; int (*p)[5] = &temp; int i; for (i = 0; i < 5; i++) { printf("%d\n", *(*p + i)); //等同 printf("%d\n", (*p)[i]); }输出:
p 是一个指向“包含5个int的数组”的指针
temp的类型是int[5](数组类型)&temp的类型是int(*)[5](指向数组的指针)p就是这种指针
所以:
p指向整个数组 temp(而不是第一个元素)*p等价于temp(数组本身)
解释*(*p + i)
*p→ 得到数组temp(在表达式中退化为指向首元素的指针&temp[0])*p + i→ 指针向后移动 i 个元素,指向temp[i]的地址
所以*p + i是指向数组中第 i 个元素的位置(地址)。
*(*p + i)→ 取出该地址的值,等价于temp[i]
数组元素指针
int temp[5] = { 2, 3, 4, 5, 6 }; int (*p) = temp;//temp 与 &temp[0] 同义 int i; for (i = 0; i < 5; i++) { printf("%d\n", *(p + i)); }输出:
指针变量加1,即向后移动1 个位置表示指针变量指向下一个数据元素的首地址。
p指向的是一个整型,p+1就是移动一个整型大小,即移动4个字节,所以p+1代表的地址比p代表的地址大4。
*(p + i) 就是取出对应地址的数据元素的值。
延申
根据 ’指针+1‘ 的运算原理,可做以下改动:
int temp[5] = { 2, 3, 4, 5, 6 }; int (*p)[5] = &temp; int i; for (i = 0; i < 5; i++) { printf("%d\n", *(p + i)); }输出:
p是数组指针,指向长度为 5 的 int 数组p + i:跳过 i 个完整数组- 每次 +1,直接跳过5 个 int
*(p + i):解引用后得到的是第 i 个数组的首地址,不是数组元素!
p指向的是拥有5个int元素的数组。拥有5个int元素的数组的数据大小为:5*4 = 20 字节;
p+i 就是 地址加上 i*20 ;
综合扩展
这里分析以下最后一个输出:*(*(&arr + 1) - 1)
int arr[3] = {1, 2, 3}; // 步骤1: &arr + 1 // &arr 是 int(*)[3] 类型,加 1 跳过整个数组(12 字节) // 假设 arr 起始地址为 0x1000,则 &arr+1 = 0x100C // 步骤2: *(&arr + 1) // 解引用 int(*)[3] 得到 int[3] 类型 // 此时 *(&arr+1) 在概念上代表"从 0x100C 开始的一个 int[3] 数组" // 但这个数组实际上并不存在(越界了),只是类型系统允许这样表达 // 步骤3: 数组名转换 // 当 *(&arr+1) 作为右值使用时,它从 int[3] 转换为 int* // 转换后得到指向"这个不存在的数组"的首元素的指针 // 也就是指向地址 0x100C 的 int* 指针 // 步骤4: -1 // int* 指针减 1,向前移动 sizeof(int) = 4 字节 // 得到地址 0x100C - 4 = 0x1008 // 0x1008 正好是 arr[2] 的地址 // 步骤5: * 解引用 // 获取 arr[2] 的地址 对应的元素数值