C语言数组遍历的方法(包含二维数组)
数组遍历的方法(包含二维数组)
文章目录
- 数组遍历的方法(包含二维数组)
- 一维数组的遍历
- 1.下标法(`[]`运算符)
- 2.指针法(`*(ptr + i)`)
- 3. 指针递增法`(ptr++)`
- 4. 尾后指针法`(begin/end模式)`
- 二维数组的遍历
- 1.双重下标法(`arr[i][j]`)
- 2.行指针法(指向一维数组的指针)
- 3.列指针法(一维化指针访问)
- 4.指针数组法(非连续内存场景)
- 关键对比与性能分析
一维数组的遍历
1.下标法([]
运算符)
- 特点:直观易懂,符合数组常规访问的习惯。
- 示例代码:
int arr[] = {1, 2, 3, 4, 5};
int len = sizeof(arr) / sizeof(arr[0]);for (int i = 0; i < len; i++)
{printf("%d ", arr[i]); // 输出:1 2 3 4 5
}
2.指针法(*(ptr + i)
)
int arr[] = {1, 2, 3, 4, 5};
int *ptr = arr;
int len = sizeof(arr) / sizeof(*ptr);for (int i = 0; i < len; i++)
{printf("%d ", *(ptr + i)); // 输出:1 2 3 4 5
}
- 代码解析:
- 指针初始化:
ptr
指向数组首地址(arr
隐式转换为指针)。偏移计算:ptr + i
表示在ptr
基础上偏移i * sizeof(int)
字节(本例为i * 4)。 - 等价性:
*(ptr + i)
完全等价于ptr[i]和arr[i]。
- 指针初始化:
- 性能分析:与下标法本质相同,但部分编译器可直接优化为寄存器操作,在高频访问时可能略快。
3. 指针递增法(ptr++)
int arr[] = {1, 2, 3, 4, 5}; // ① 定义并初始化一维整型数组,包含5个元素
int* ptr = arr; // ② 将数组名`arr`赋值给指针`ptr`,数组名隐式转换为指向首元素的指针(int*类型),ptr指向arr[0](地址0x7fff...)
int len = sizeof(arr) / sizeof(*ptr); // ③ 计算数组长度:// sizeof(arr)获取数组总字节数(5*4=20字节),sizeof(*ptr)为单个元素字节数(4字节),len=20/4=5for (int i = 0; i < len; i++, ptr++) // ④ 循环条件:// i从0开始,当i<5时执行循环体// 每次循环后i自增1,ptr自增1(指针移动步长为sizeof(int)=4字节,指向下一个元素)
{printf("%d ", *ptr); // ⑤ 解引用指针ptr,输出当前指向的元素值// 第一次循环:ptr指向arr[0],输出1;第二次循环:ptr指向arr[1],输出2;依此类推
}
// 恢复指针(如需再次使用):ptr = arr; // ⑥ 循环结束后,ptr指向arr[5](数组末尾的下一个位置)// 若需再次遍历,需将ptr重置为数组首地址arr(指向arr[0])
- 代码解析:
- 指针移动:每次循环
ptr
自增,直接指向下一个元素(步长为4字节) - 副作用:遍历结束后ptr指向数组末尾的下一个位置(如&arr[5]),需重置才能再次使用。
- 指针移动:每次循环
4. 尾后指针法(begin/end模式)
int arr[] = {1, 2, 3, 4, 5}; // ① 定义并初始化一维整型数组,包含5个元素(静态分配,内存连续)
int *begin = arr; // ② 将数组名`arr`赋值给指针`begin`,数组名隐式转换为指向首元素的指针(int*类型)// begin指向arr[0](地址0x7fff...),作为遍历起点
int *end = arr + sizeof(arr)/sizeof(arr[0]); // ③ 计算尾后指针`end`:// sizeof(arr)/sizeof(arr[0])得到数组元素个数(5)// arr + 5 指向arr[5](数组最后一个元素的下一个位置,即尾后位置)// end不可解引用(访问该地址会导致越界),仅用于边界判断for (int *ptr = begin; ptr < end; ptr++) // ④ 循环条件:// ptr初始化为begin(指向arr[0])// 每次循环检查ptr是否小于end(即是否未到达尾后位置)// 每次循环结束后ptr自增1(指向下一个元素,步长为4字节)
{printf("%d ", *ptr); // ⑤ 解引用指针ptr,输出当前指向的元素值// 第一次循环:ptr指向arr[0],输出1;第二次循环:ptr指向arr[1],输出2;依此类推
}
// 循环结束后,ptr == end(指向arr[5])
- 代码解析:
- 尾后指针:end指向数组最后一个元素的下一个位置(不指向有效数据)。
- 安全边界:ptr < end确保不会越界,即使数组长度动态变化也无需修改循环条件。
二维数组的遍历
1.双重下标法(arr[i][j]
)
int arr[3][4] =
{{1, 2, 3, 4},{5, 6, 7, 8},{9, 10, 11, 12}
};for (int i = 0; i < 3; i++)
{for (int j = 0; j < 4; j++) {printf("%d ", arr[i][j]); // 逐行输出}printf("\n");
}
-
内存布局
- C 语言中二维数组按行优先存储,内存中排列:1,2,3,4,5,6,7,8,9,10,11,12。
-
访问原理:
arr[i][j]
等价于*(*arr + i) + j)
- 计算步骤:
arr + i:
偏移i行(每行4 * 4 = 16字节)*(arr + i):
解引用得到第i行的首地址*(arr + i) + j:
在第i行基础上偏移j个元素*(*(arr + i) + j):
解引用得到具体元素
2.行指针法(指向一维数组的指针)
int arr[3][4] = {...};
int (*row_ptr)[4] = arr; // 行指针,指向包含4个int的一维数组for (int i = 0; i < 3; i++)
{for (int j = 0; j < 4; j++) {printf("%d ", (*row_ptr)[j]); // 解引用行指针后取下标}row_ptr++; // 指向下一行
}
- 代码解析:
行指针声明
:int (*row_ptr)[4]表示row_ptr是一个指针,指向包含4个int的数组。行偏移
:row_ptr++会使指针跳过4 * 4 = 16字节(一行的大小)。等价写法
:(*row_ptr)[j]等价于row_ptr[0][j],即当前行的第j个元素。优势
:保持二维结构的逻辑,适合按行处理数据(如矩阵转置)。
3.列指针法(一维化指针访问)
int arr[3][4] = {...};
int *col_ptr = &arr[0][0]; // 指向首元素
int rows = 3, cols = 4;for (int i = 0; i < rows * cols; i++)
{printf("%d ", col_ptr[i]); // 等价于*(col_ptr + i)
}
-
内存映射:
-
将二维数组视为长度为
rows * cols
的一维数组。 -
元素
arr[i][j]
对应一维索引i * cols + j
(本例中arr[1][2]
对应1 * 4 + 2 = 6
)。
-
-
性能分析
- 内存连续访问,缓存命中率高。
- 但需手动计算行列坐标(如需还原)
int row = i / cols;
int col = i % cols;
4.指针数组法(非连续内存场景)
// 动态分配3行,每行4列
int **arr = (int **)malloc(3 * sizeof(int *));
for (int i = 0; i < 3; i++)
{arr[i] = (int *)malloc(4 * sizeof(int));for (int j = 0; j < 4; j++){arr[i][j] = i * 4 + j + 1; // 赋值}
}// 遍历
for (int i = 0; i < 3; i++)
{for (int j = 0; j < 4; j++) {printf("%d ", arr[i][j]);}free(arr[i]); // 释放每行内存
}
free(arr); // 释放指针数组
-
内存结构:
- arr是一个指针数组,每个元素arr[i]指向一行数据。
- 每行内存可动态分配不同长度(如arr[0]指向 2 个元素,arr[1]指向 3 个元素)。
-
访问过程:
arr
是一级指针,指向指针数组首地址。arr[i]
是二级指针,指向第i行的一维数组。arr[i][j]
通过两次解引用访问具体元素。
-
注意事项:
- 每行内存地址可能不连续,随机访问效率较低。
- 必须严格按顺序释放内存(先释放每行,再释放指针数组)。