- C编程教程
- C - 首页
- C语言基础
- C - 概述
- C - 特性
- C - 历史
- C - 环境设置
- C - 程序结构
- C - Hello World
- C - 编译过程
- C - 注释
- C - 词法单元
- C - 关键字
- C - 标识符
- C - 用户输入
- C - 基本语法
- C - 数据类型
- C - 变量
- C - 整数提升
- C - 类型转换
- C - 类型强制转换
- C - 布尔值
- C语言中的常量和字面量
- C - 常量
- C - 字面量
- C - 转义序列
- C - 格式说明符
- C语言中的运算符
- C - 运算符
- C - 算术运算符
- C - 关系运算符
- C - 逻辑运算符
- C - 位运算符
- C - 赋值运算符
- C - 一元运算符
- C - 自增和自减运算符
- C - 三元运算符
- C - sizeof 运算符
- C - 运算符优先级
- C - 其他运算符
- C语言中的决策
- C - 决策
- C - if 语句
- C - if...else 语句
- C - 嵌套 if 语句
- C - switch 语句
- C - 嵌套 switch 语句
- C语言中的循环
- C - 循环
- C - while 循环
- C - for 循环
- C - do...while 循环
- C - 嵌套循环
- C - 无限循环
- C - break 语句
- C - continue 语句
- C - goto 语句
- C语言中的函数
- C - 函数
- C - 主函数
- C - 按值调用函数
- C - 按引用调用函数
- C - 嵌套函数
- C - 可变参数函数
- C - 用户自定义函数
- C - 回调函数
- C - 返回语句
- C - 递归
- C语言中的作用域规则
- C - 作用域规则
- C - 静态变量
- C - 全局变量
- C语言中的数组
- C - 数组
- C - 数组的特性
- C - 多维数组
- C - 将数组传递给函数
- C - 从函数返回数组
- C - 可变长数组
- C语言中的指针
- C - 指针
- C - 指针和数组
- C - 指针的应用
- C - 指针运算
- C - 指针数组
- C - 指向指针的指针
- C - 将指针传递给函数
- C - 从函数返回指针
- C - 函数指针
- C - 指向数组的指针
- C - 指向结构体的指针
- C - 指针链
- C - 指针与数组
- C - 字符指针和函数
- C - 空指针
- C - void 指针
- C - 悬空指针
- C - 解引用指针
- C - 近、远和巨型指针
- C - 指针数组的初始化
- C - 指针与多维数组
- C语言中的字符串
- C - 字符串
- C - 字符串数组
- C - 特殊字符
- C语言中的结构体和联合体
- C - 结构体
- C - 结构体和函数
- C - 结构体数组
- C - 自引用结构体
- C - 查找表
- C - 点 (.) 运算符
- C - 枚举 (或 enum)
- C - 结构体填充和打包
- C - 嵌套结构体
- C - 匿名结构体和联合体
- C - 联合体
- C - 位域
- C - Typedef
- C语言中的文件处理
- C - 输入 & 输出
- C - 文件 I/O (文件处理)
- C预处理器
- C - 预处理器
- C - 指令
- C - 预处理器运算符
- C - 宏
- C - 头文件
- C语言中的内存管理
- C - 内存管理
- C - 内存地址
- C - 存储类
- 其他主题
- C - 错误处理
- C - 可变参数
- C - 命令执行
- C - 数学函数
- C - static 关键字
- C - 随机数生成
- C - 命令行参数
- C编程资源
- C - 问答
- C - 快速指南
- C - 速查表
- C - 有用资源
- C - 讨论
C语言中的指针和多维数组
在 C 语言中,数组是存储在连续内存位置中的一组相同类型的值的集合。数组(一维或多维)中的每个元素都由一个或多个唯一的整数索引标识。
另一方面,指针存储变量的地址。数组中第 0 个元素的地址是数组的指针。您可以使用“解引用运算符”访问指针指向的值。
您可以在 C 中声明一维、二维或多维数组。“维度”一词指的是识别集合中元素所需的索引数。
指针和一维数组
在一维数组中,每个元素都由一个整数标识
int a[5] = {1, 2, 3, 4, 5};
这里,数字“1”位于索引 0 处,“2”位于索引 1 处,依此类推。
存储第 0 个元素地址的变量是它的指针 -
int *x = &a[0];
简单来说,数组的名称也指向第 0 个元素的地址。因此,您还可以使用此表达式 -
int *x = a;
示例
由于指针的值会根据数据类型的尺寸递增,“x++”会将指针移动到数组中的下一个元素。
#include <stdio.h>
int main(){
int arr[] = {1, 2, 3, 4, 5};
int length = sizeof(arr) / sizeof(arr[0]);
int i = 0;
int *ptr = arr;
while (i < length){
printf("arr[%d]: %d \n", i, *(ptr + i));
i++;
}
return 0;
}
输出
运行此代码时,将产生以下输出 -
arr[0]: 1 arr[1]: 2 arr[2]: 3 arr[3]: 4 arr[4]: 5
指针和二维数组
如果一维数组类似于元素列表,则二维数组类似于表格或矩阵。
二维数组中的元素可以被认为是在逻辑上以行和列排列的。因此,任何元素的位置都由两个索引决定,即它的行号和列号。行索引和列索引都从“0”开始。
int arr[2][2];
这样的数组表示为 -
| Col0 | Col1 | Col2 | |
|---|---|---|---|
| Row0 | arr[0][0] | arr[0][1] | arr[0][2] |
| Row1 | arr[1][0] | arr[1][1] | arr[1][2] |
| Row2 | arr[2][0] | arr[2][1] | arr[2][2] |
需要注意的是,表格排列仅是一种逻辑表示。编译器分配一块连续的字节。在 C 中,数组分配以行优先的方式进行,这意味着元素以行方式读入数组。
这里,我们声明一个具有三行四列的二维数组(第一个方括号中的数字始终指代行数)如下 -
int arr[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
编译器将以行优先的顺序为上述二维数组分配内存。假设数组的第一个元素位于地址 1000 处,并且类型“int”的大小为 4 字节,则数组的元素将获得以下分配的内存位置 -
| Row 0 | Row 1 | Row 2 | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 值 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 地址 | 1000 | 1004 | 1008 | 1012 | 1016 | 1020 | 1024 | 1028 | 1032 | 1036 | 1040 | 1044 |
我们将使用地址 & 运算符将数组 num 的第一个元素的地址分配给指针 ptr。
int *ptr = &arr[0][0];
示例 1
如果指针递增 1,它将移动到下一个地址。“3×4”数组中的所有 12 个元素都可以在循环中访问,如下所示 -
#include <stdio.h>
int main(){
int arr[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12},
};
// pointer ptr pointing at array num
int *ptr = &arr[0][0];
int i, j, k = 0;
// print the elements of the array num via pointer ptr
for (i = 0; i < 3; i++){
for (j = 0; j < 4; j++){
printf("%d ", *(ptr + k));
k++;
}
printf("\n");
}
return 0;
}
输出
运行此代码时,将产生以下输出 -
1 2 3 4 5 6 7 8 9 10 11 12
通常,可以使用以下公式获得数组中任何元素的地址 -
add of element at ith row and jth col = baseAddress + [(i * no_of_cols + j) * sizeof(array_type)]
在我们的 3×4 数组中,
add of arr[2][4] = 1000 + (2*4 + 2)*4 = 1044
您可以参考上图,它确认“arr[3][4]”的地址是 1044。
示例 2
使用解引用指针获取地址处的值。让我们使用此公式在指针的帮助下遍历数组 -
#include <stdio.h>
int main(){
// 2d array
int arr[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
int ROWS = 3, COLS = 4;
int i, j;
// pointer
int *ptr = &arr[0][0];
// print the element of the array via pointer ptr
for (i = 0; i < ROWS; i++){
for (j = 0; j < COLS; j++) {
printf("%4d ",*(ptr + (i * COLS + j)));
}
printf("\n");
}
return 0;
}
输出
运行此代码时,将产生以下输出 -
1 2 3 4 5 6 7 8 9 10 11 12
指针和三维数组
三维数组是二维数组的数组。此类数组用三个下标声明 -
int arr [x] [y] [j];
此数组可以被视为“x”个层的表格,每个表格具有“x”行和“y”列。
三维数组的一个示例是 -
int arr[3][3][3] ={
{ {11, 12, 13}, {14, 15, 16}, {17, 18, 19} },
{ {21, 22, 23}, {24, 25, 26}, {27, 28, 29} },
{ {31, 32, 33}, {34, 35, 36}, {37, 38, 39} },
};
指向三维数组的指针可以声明为 -
int * ptr = &arr[0][0][0];
知道数组的名称本身就是第 0 个元素的地址,我们可以将三维数组的指针写为 -
int * ptr = arr;
“x”行和“y”列的每一层占用 -
x * y * sizeof(data_type)
字节数。假设分配给上面声明的三维数组“arr”的内存从地址 1000 开始,则第二层(i = 1)从 1000 + (3 × 3) × 4 = 1036 字节位置开始。
ptr = Base address of 3D array arr
如果 JMAX 是行数,KMAX 是列数,则第 1 个切片的第 0 行和第 0 列的元素的地址为 -
arr[1][0][0] = ptr + (1 * JMAX * KMAX)
获取第 i 个切片的第 j 行和第 k 列的元素值的公式可以给出如下 -
arr[i][j][k] = *(ptr + (i * JMAX*KMAX) + (j*KMAX + k))
示例:使用指针解引用打印三维数组
让我们使用此公式在指针解引用的帮助下打印三维数组 -
#include <stdio.h>
int main(){
int i, j, k;
int arr[3][3][3] = {
{ {11, 12, 13}, {14, 15, 16}, {17, 18, 19} },
{ {21, 22, 23}, {24, 25, 26}, {27, 28, 29} },
{ {31, 32, 33}, {34, 35, 36}, {37, 38, 39} },
};
int JMAX = 3, KMAX = 3;
int *ptr = arr; // &arr[0][0][0];
for(i = 0; i < 3; i++){
for(j = 0; j < 3; j++){
for(k = 0; k < 3; k++){
printf("%d ",*(ptr+(i*JMAX*KMAX)+(j*KMAX+k)));
}
printf("\n");
}
printf("\n");
}
return 0;
}
输出
运行此代码时,将产生以下输出 -
11 12 13 14 15 16 17 18 19 21 22 23 24 25 26 27 28 29 31 32 33 34 35 36 37 38 39
通常,使用指针访问数组与使用下标表示访问数组非常相似。两者之间的主要区别在于,数组的下标声明以静态方式分配内存,而我们可以使用指针进行动态内存分配。
要将多维数组传递给函数,您需要使用指针而不是下标。但是,使用下标数组比使用指针更方便,对于初学者来说可能比较困难。