C语言中数组类型指针的特点
一维数组类型
在 C 语言中,若限定 arr 为一维数组,并声明 int (*q)[3] = &arr;,则 q 和 *q 的行为与多维数组场景下有所不同。以下是具体分析:
1. 类型与含义
(1) q 的类型与含义
- 类型:
int (*)[3],即指向长度为 3 的整型数组的指针。 - 值:
q存储的是整个一维数组arr的地址(即&arr)。int arr[3] = {3, 2, 1}; int (*q)[3] = &arr; // q 指向整个数组 arr //等价于下面 typedef int intArr[3]; intArr arr = {3, 2, 1}; intArr *q = &arr; // 数组类型指针
(2) *q 的类型与含义
- 类型:
int[3](数组类型),但在表达式中会自动退化为int*(指向数组首元素的指针)。 - 值:
*q等价于arr,即数组首元素的地址(&arr[0])。*q; // 退化为 int*,指向 arr[0] *q + 1; // 指向 arr[1]
2. q 与 *q 的对比
| 特性 | q |
*q |
|---|---|---|
| 类型 | int (*)[3](数组指针) |
int[3](退化为 int*) |
| 值的地址 | &arr(整个数组的地址) |
&arr[0](首元素的地址) |
| 指针运算步长 | 3 * sizeof(int)(跳过一个数组) |
sizeof(int)(跳过一个元素) |
| 用途 | 操作整个数组的地址 | 操作数组元素 |
3. 关键操作分析
(1) 访问元素
-
通过
q访问数组元素:(*q)[0]; // 等价于 arr[0] → 3 (*q)[1]; // 等价于 arr[1] → 2或使用数组指针语法:
q[0][0]; // 等价于 (*q)[0] → 3 -
通过
*q访问元素(退化为int*):*(*q); // 等价于 arr[0] → 3 *(*q + 1); // 等价于 arr[1] → 2
(2) 指针运算
-
q + 1:
跳过整个数组(3 个int),指向内存中紧邻arr的下一个位置(可能越界)。q + 1; // 地址增加 3 * sizeof(int) -
*q + 1:
跳过一个元素(int),指向arr[1]。*q + 1; // 地址增加 sizeof(int)
4. 示例代码
#include <stdio.h>
int main() {
int arr[3] = {3, 2, 1};
int (*q)[3] = &arr; // q 指向整个数组 arr
// 通过 q 访问元素
printf("(*q)[0] = %d\n", (*q)[0]); // 输出 3
printf("q[0][1] = %d\n", q[0][1]); // 输出 2
// 通过 *q 访问元素(退化为 int*)
printf("*(*q + 2) = %d\n", *(*q + 2)); // 输出 1
// 验证地址一致性
printf("q 的地址: %p\n", (void*)q); // 输出 &arr
printf("*q 的地址: %p\n", (void*)*q); // 输出 &arr[0]
// 指针运算
printf("q + 1 的地址: %p\n", (void*)(q + 1)); // 跳过一个数组(12 字节)
printf("*q + 1 的地址: %p\n", (void*)(*q + 1)); // 跳过一个元素(4 字节)
return 0;
}
5. 常见误区
-
误用指针类型
q是数组指针,*q是数组(退化为int*)。- 错误示例:
int *p = q; // 错误!q 是 int(*)[3],不能直接赋值给 int* int *p = *q; // 正确!*q 退化为 int*
-
混淆
q和*q的指针运算q + 1和*q + 1的步长不同,前者针对整个数组,后者针对单个元素。
总结
q:指向整个一维数组的指针,类型为int (*)[3],用于操作数组的地址。*q:退化为指向数组首元素的指针(int*),用于操作数组元素。- 虽然
q和*q的地址值相同(&arr和&arr[0]数值相同),但类型不同,导致指针运算和用途的差异。
二维数组类型
在 C 语言中,若限定 arr 为二维数组,例如 int arr[2][3] = {{1, 2, 3}, {4, 5, 6}};,并声明 int (*q)[3] = arr;
1. 类型与含义
(1) q 的类型与含义
- 类型:
int (*)[3],即指向长度为 3 的整型数组的指针。 - 值:
q存储的是二维数组arr的第一行的地址(即arr[0]的地址)。int arr[2][3] = {{1, 2, 3}, {4, 5, 6}}; int (*q)[3] = arr; // q 指向 arr 的第一行(类型为 int[3])
(2) *q 的类型与含义
- 类型:
int[3](数组类型),但在表达式中会退化为int*(指向数组首元素的指针)。 - 值:
*q等价于arr[0],即二维数组第一行的首元素地址(&arr[0][0])。*q; // 退化为 int*,指向 arr[0][0] *q + 1; // 指向 arr[0][1]
2. q 与 *q 的对比
| 特性 | q |
*q |
|---|---|---|
| 类型 | int (*)[3](数组指针) |
int[3](退化为 int*) |
| 值的地址 | &arr[0](第一行的地址) |
&arr[0][0](首元素的地址) |
| 指针运算步长 | 3 * sizeof(int)(跳过一行) |
sizeof(int)(跳过一个元素) |
| 用途 | 操作二维数组的行 | 操作二维数组的列(元素) |
3. 关键操作分析
(1) 访问元素
-
通过
q访问元素:q[0][0]; // 等价于 arr[0][0] → 1 q[1][2]; // 等价于 arr[1][2] → 6或使用解引用语法:
(*q)[0]; // 等价于 arr[0][0] → 1 *(*(q + 1) + 2); // 等价于 arr[1][2] → 6 -
通过
*q访问元素(退化为int*):*(*q); // 等价于 arr[0][0] → 1 *(*q + 1); // 等价于 arr[0][1] → 2 *(*(q + 1) + 2); // 等价于 arr[1][2] → 6
(2) 指针运算
-
q + 1:
跳转到二维数组的下一行(即arr[1]的地址)。q + 1; // 地址增加 3 * sizeof(int)(例如,从 0x1000 到 0x100C,假设 int 为 4 字节) -
*q + 1:
跳转到当前行的下一个元素(即arr[0][1]的地址)。*q + 1; // 地址增加 sizeof(int)(例如,从 0x1000 到 0x1004)
4. 示例代码
#include <stdio.h>
int main() {
int arr[2][3] = {{1, 2, 3}, {4, 5, 6}};
int (*q)[3] = arr; // q 指向 arr 的第一行
// 通过 q 访问元素
printf("q[0][0] = %d\n", q[0][0]); // 输出 1
printf("*(*(q + 1) + 2) = %d\n", *(*(q + 1) + 2)); // 输出 6
// 通过 *q 访问元素(退化为 int*)
printf("*(*q + 1) = %d\n", *(*q + 1)); // 输出 2
printf("*(*(q + 1)) = %d\n", *(*(q + 1))); // 输出 4(第二行首元素)
// 验证地址一致性
printf("q 的地址: %p\n", (void*)q); // 输出 &arr[0]
printf("*q 的地址: %p\n", (void*)*q); // 输出 &arr[0][0]
// 指针运算
printf("q + 1 的地址: %p\n", (void*)(q + 1)); // 跳转到第二行
printf("*q + 1 的地址: %p\n", (void*)(*q + 1)); // 跳转到第一行第二个元素
return 0;
}
5. 常见误区
-
误将
q赋值给int**int **p = q; // 错误!q 是 int(*)[3],而 int** 是指向指针的指针正确做法:
int *p = *q; // 正确!*q 退化为 int* -
混淆
q和*q的指针运算q + 1跳转一行,*q + 1跳转一列。- 错误示例:
int *p = q + 1; // 错误!q + 1 是 int(*)[3],不能赋值给 int*
总结
q:指向二维数组某一行的指针(类型为int (*)[3]),用于按行操作二维数组。*q:退化为指向当前行首元素的指针(类型为int*),用于按列操作元素。- 关键区别:
q的步长是一行的长度(3 * sizeof(int)),适合遍历行。*q的步长是一列的长度(sizeof(int)),适合遍历列。
N维数组类型
在 C 语言中,若 arr 是一个 N 维数组(例如 int arr[D1][D2][D3]...[DN];),则声明 int (*q)[D2][D3]...[DN] = arr; 会定义一个指向 N-1 维子数组的指针 q。理解 q 和 *q 的行为需要逐层分析多维数组的指针机制。以下是针对 N 维数组的详细分析:
1. 类型与含义
(1) q 的类型与含义
- 类型:
int (*)[D2][D3]...[DN],即指向一个 N-1 维子数组 的指针。- 例如,若
arr是三维数组int arr[D1][D2][D3];,则q的类型为int (*)[D2][D3]。
- 例如,若
- 值:
q存储的是 N 维数组arr的第一个 N-1 维子数组的地址(即&arr[0])。int arr[D1][D2][D3]...[DN]; int (*q)[D2][D3]...[DN] = arr; // q 指向 arr[0]
(2) *q 的类型与含义
- 类型:
int [D2][D3]...[DN](一个 N-1 维数组),但在表达式中会自动退化为指向其首元素的指针(即int (*)[D3]...[DN])。- 例如,若
q是三维数组指针int (*q)[D2][D3],则*q的类型为int [D2][D3],退化为int (*)[D3]。
- 例如,若
- 值:
*q等价于arr[0],即第一个 N-1 维子数组的首元素地址。
2. q 与 *q 的对比
| 特性 | q |
*q |
|---|---|---|
| 类型 | int (*)[D2][D3]...[DN](N-1 维指针) |
int [D2][D3]...[DN](退化为 N-2 维指针) |
| 值的地址 | &arr[0](第 1 层子数组的地址) |
arr[0](第 2 层子数组的地址) |
| 指针运算步长 | sizeof(int) * D2 * D3 * ... * DN |
sizeof(int) * D3 * ... * DN |
| 用途 | 按第 1 层维度遍历(如行、面等) | 按第 2 层维度遍历(如列、行等) |
3. 关键操作分析
(1) 访问元素
-
通过
q访问元素:// 例如三维数组: q[i][j][k]; // 等价于 arr[i][j][k] // 或通过解引用: (*(*(q + i) + j) + k); // 等价于 arr[i][j][k] -
通过
*q访问元素:// *q 退化为指向第 2 层子数组的指针(N-2 维) (*q)[j][k]; // 等价于 arr[0][j][k] *(*(*q + j) + k); // 等价于 arr[0][j][k]
(2) 指针运算
-
q + 1:
跳转到下一个 N-1 维子数组(即arr[1]的地址),步长为D2 * D3 * ... * DN * sizeof(int)。q + 1; // 地址增加 sizeof(int) * D2 * D3 * ... * DN -
*q + 1:
跳转到当前 N-1 维子数组的下一个 N-2 维子数组(即arr[0][1]的地址),步长为D3 * ... * DN * sizeof(int)。*q + 1; // 地址增加 sizeof(int) * D3 * ... * DN
4. 通用规则(N 维数组)
-
指针类型逐层退化
- 每解引用一次指针
q,维度降低一层,类型从int (*)[Dk][Dk+1]...[DN]退化为int (*)[Dk+1]...[DN]。 - 最终解引用到最后一层时,退化为
int*。
- 每解引用一次指针
-
步长逐层递减
- 第
k层指针的步长为sizeof(int) * Dk * Dk+1 * ... * DN。 - 每解引用一层,步长减少一个维度因子(例如从
Dk到Dk+1)。
- 第
-
元素访问公式
- 对于 N 维数组
arr[D1][D2]...[DN],元素arr[i1][i2]...[iN]的地址可通过指针逐层计算:*(*(...*(q + i1) + i2) + ...) + iN);
- 对于 N 维数组
5. 示例(三维数组)
#include <stdio.h>
int main() {
int arr[2][3][4] = {
{{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}},
{{13, 14, 15, 16}, {17, 18, 19, 20}, {21, 22, 23, 24}}
};
int (*q)[3][4] = arr; // q 指向第一个二维子数组(类型为 int[3][4])
// 通过 q 访问元素
printf("q[1][2][3] = %d\n", q[1][2][3]); // 输出 24
// 通过 *q 访问元素(退化为 int(*)[4])
printf("(*q)[2][3] = %d\n", (*q)[2][3]); // 输出 12
// 指针运算
printf("q + 1 的地址差: %ld\n", (char*)(q + 1) - (char*)q); // 输出 3*4*sizeof(int) = 48(假设 int 为 4 字节)
printf("*q + 1 的地址差: %ld\n", (char*)(*q + 1) - (char*)*q); // 输出 4*sizeof(int) = 16
return 0;
}
6. 常见误区
-
类型不匹配
- 若将
q赋值给低维指针(如int*),需显式解引用至最后一层:int *p = **q; // 正确!**q 退化为 int*
- 若将
-
越界访问
- 多维数组的指针运算需严格计算维度步长,否则可能访问到无效内存。
-
混淆指针层级
- 对
q和*q的指针运算需明确当前操作的维度层级。
- 对
总结
q:指向 N 维数组的第 1 层子数组(N-1 维),用于按最高维度遍历(如三维数组中的“层”或“面”)。*q:退化为指向第 2 层子数组(N-2 维),用于按次高层维度遍历(如三维数组中的“行”)。- 核心规则:
- 每解引用一次指针,维度降低一层,步长减少一个维度因子。
- 类型和步长的层级关系是操作高维数组的关键。

浙公网安备 33010602011771号