C语言中一维指针、二维指针和三维指针

news/2025/2/19 8:45:37/

指针可以指向一份普通类型的数据,例如 int、double、char 等,也可以指向一份指针类型的数据,例如 int *double *char * 等。

如果一个指针指向的是另外一个指针,我们就称它为二级指针,或者指向指针的指针。

假设有一个 int 类型的变量 a,p1是指向 a 的指针变量,p2 又是指向 p1 的指针变量,它们的关系如下图所示:
在这里插入图片描述
将这种关系转换为C语言代码:

int a =100;
int *p1 = &a;
int **p2 = &p1;

指针变量也是一种变量,也会占用存储空间,也可以使用&获取它的地址。C语言不限制指针的级数,每增加一级指针,在定义指针变量时就得增加一个星号*p1 是一级指针,指向普通类型的数据,定义时有一个*p2 是二级指针,指向一级指针 p1,定义时有两个*

如果我们希望再定义一个三级指针 p3,让它指向 p2,那么可以这样写:

int ***p3 = &p2;

四级指针也是类似的道理:

int ****p4 = &p3;

想要获取指针指向的数据时,一级指针加一个*,二级指针加两个*,三级指针加三个*,以此类推,请看代码:

#include <stdio.h>
int main(){int a =100;int *p1 = &a;int **p2 = &p1;int ***p3 = &p2;printf("%d, %d, %d, %d\n", a, *p1, **p2, ***p3);printf("&p2 = %#X, p3 = %#X\n", &p2, p3);printf("&p1 = %#X, p2 = %#X, *p3 = %#X\n", &p1, p2, *p3);printf(" &a = %#X, p1 = %#X, *p2 = %#X, **p3 = %#X\n", &a, p1, *p2, **p3);return 0;
}

运行结果:

100, 100, 100, 100
&p2 = 0X28FF3C, p3 = 0X28FF3C
&p1 = 0X28FF40, p2 = 0X28FF40, *p3 = 0X28FF40&a = 0X28FF44, p1 = 0X28FF44, *p2 = 0X28FF44, **p3 = 0X28FF44

*p3 得到的是 p2 的值,也即 p1 的地址;*(*p3) 得到的是 p1 的值,也即 a 的地址;经过三次“取值”操作后,*(*(*p3)) 得到的才是 a 的值。



指针数组

如果一个数组中的所有元素保存的都是指针,那么我们就称它为指针数组。指针数组的定义形式一般为:

dataType *arrayName[length];

[ ] 的优先级高于* ,该定义形式应该理解为:
dataType *(arrayName[length]);

括号里面说明 arrayName 是一个数组,包含了 length 个元素,括号外面说明每个元素的类型为 dataType *

除了每个元素的数据类型不同,指针数组和普通数组在其他方面都是一样的,下面是一个简单的例子:

#include <stdio.h>
int main(){int a = 16, b = 932, c = 100;//定义一个指针数组int *arr[3] = {&a, &b, &c};//也可以不指定长度,直接写作 int *arr[]//定义一个指向指针数组的指针int **parr = arr;printf("%d, %d, %d\n", *arr[0], *arr[1], *arr[2]);printf("%d, %d, %d\n", **(parr+0), **(parr+1), **(parr+2));return 0;
}

运行结果:

16, 932, 100
16, 932, 100

arr 是一个指针数组,它包含了 3 个元素,每个元素都是一个指针,在定义 arr 的同时,我们使用变量 a、b、c 的地址对它进行了初始化,这和普通数组是多么地类似。

parr 是指向数组 arr 的指针,确切地说是指向 arr 第 0 个元素的指针,它的定义形式应该理解为 int *(*parr) ,括号中的 * 表示 parr 是一个指针,括号外面的 int * 表示 parr 指向的数据的类型。arr 第 0 个元素的类型为 int * ,所以在定义 parr 时要加两个 *

第一个 printf() 语句中,arr[i] 表示获取第 i 个元素的值,该元素是一个指针,还需要在前面增加一个 * 才能取得它指向的数据,也即 *arr[i] 的形式。

第二个printf() 语句中,parr+i 表示第 i 个元素的地址,*(parr+i) 表示获取第 i个元素的值(该元素是一个指针),**(parr+i) 表示获取第 i 个元素指向的数据。

指针数组还可以和字符串数组结合使用,请看下面的例子:

#include <stdio.h>
int main(){char *str[3] = {"c.biancheng.net","C语言中文网","C Language"};printf("%s\n%s\n%s\n", str[0], str[1], str[2]);return 0;
}

运行结果:

c.biancheng.net
C语言中文网
C Language

需要注意的是,字符数组 str 中存放的是字符串的首地址,不是字符串本身,字符串本身位于其他的内存区域,和字符数组是分开的。

也只有当指针数组中每个元素的类型都是 char * 时,才能像上面那样给指针数组赋值,其他类型不行。

为了便于理解,可以将上面的字符串数组改成下面的形式,它们都是等价的。

#include <stdio.h>
int main(){char *str0 = "c.biancheng.net";char *str1 = "C语言中文网";char *str2 = "C Language";char *str[3] = {str0, str1, str2};printf("%s\n%s\n%s\n", str[0], str[1], str[2]);return 0;
}


当涉及到多维数据结构时,C语言提供了一维、二维和三维指针来处理这些数据。指针是一种特殊类型的变量,它存储了内存地址,可以用于访问和操作内存中的数据。本文将详细介绍C语言中一维指针、二维指针和三维指针的内存结构,并提供相应的C语言示例来加深理解。

一维指针

一维指针是处理一维数组的重要工具。它存储数组的首个元素的内存地址,并可用于遍历整个数组。一维数组在内存中是连续存储的,因此一维指针可以按顺序访问数组的各个元素。

int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr;  // 定义一维指针并指向数组的首个元素for (int i = 0; i < 5; i++) {printf("%d ", *(ptr + i));
}

上述示例中,我们定义了一个包含5个元素的整型数组arr,然后定义了一个指向 arr 的一维指针 ptr。通过指针ptr,我们使用指针算术运算来遍历数组,并使用间接引用运算符*访问每个元素的值。

二维指针

二维指针用于处理二维数组,它存储了二维数组每个元素的内存地址。二维数组在内存中以行优先的方式存储,即每一行的元素是连续存储的。二维指针可以通过指向每个一维数组的指针来访问和操作二维数组的元素。

int matrix[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
int **ptr = (int **)malloc(3 * sizeof(int *));for (int i = 0; i < 3; i++) {ptr[i] = matrix[i];
}for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {printf("%d ", *(*(ptr + i) + j));}printf("\n");
}free(ptr);

在上述示例中,我们定义了一个包含3行3列的二维整型数组matrix。然后,我们使用二维指针 ptr 动态分配了3个指针的内存空间,并将每一行的首地址赋给二维指针。通过二维指针 ptr,我们使用指针算术运算来访问和操作二维数组的元素。

三维指针

三维指针用于处理三维数组,它存储了三维数组每个元素的内存地址。三维数组在内存中的存储方式比较复杂,它需要使用多级指针来表示。通过多级指针,我们可以访问和操作三维数组的元素。

int cube[2][2][2] = {{{1, 2}, {3, 4}}, {{5, 6}, {7, 8}}};
int ***ptr = (int ***)malloc(2 * sizeof(int **));for (int i = 0; i < 2; i++) {ptr[i] = (int **)malloc(2 * sizeof(int *));for (int j = 0; j < 2; j++) {ptr[i][j] = (int *)malloc(2 * sizeof(int));for (int k = 0; k < 2; k++) {ptr[i][j][k] = cube[i][j][k];}}
}for (int i = 0; i < 2; i++) {for (int j = 0; j < 2; j++) {for (int k = 0; k < 2; k++) {printf("%d ", *(*(*(ptr + i) + j) + k));}printf("\n");}printf("\n");
}for (int i = 0; i < 2; i++) {for (int j = 0; j < 2; j++) {free(ptr[i][j]);}free(ptr[i]);
}
free(ptr);

在上述示例中,我们定义了一个包含2个2行2列的三维整型数组cube。然后,我们使用三维指针 ptr 动态分配了对应的内存空间,并将每个元素的值赋给三维指针。通过三维指针 ptr,我们使用指针算术运算来访问和操作三维数组的元素。


http://www.ppmy.cn/news/1207597.html

相关文章

Triples of Cows

题目传送门 引 模拟赛 T 4 T4 T4 , 变换挺妙的, 而且感觉转换后问题就迎刃而解了 解法 强行模拟拆点重连边显然不行,会让图的边数达到 n 2 n^2 n2 级别的 —————————————————————————————————————————————————— 考虑转…

晶振分频【FPGA】

所有数据对齐晶振。 6分频&#xff1a;【1】 module divider_six // 6分频 【0~2】 ( input wire sys_clk , //系统时钟 50MHz input wire sys_rst_n , //全局复位 output reg clk_out //对系统时钟 6 分频后的信号 );reg [1:0] cnt; //用于计数的寄存器 //cnt:计数器从 0 到…

【STM32】STM32的Cube和HAL生态

1.单片机软件开发的时代变化 1.单片机的演进过程 (1)第1代&#xff1a;4004、8008、Zilog那个年代&#xff08;大约1980年代之前&#xff09; (2)第2代&#xff1a;51、PIC8/16、AVR那个年代&#xff08;大约2005年前&#xff09; (3)第3代&#xff1a;51、PIC32、Cortex-M0、…

EMQX服务器的API接口使用介绍_完成上位机开发

一、前言 在前面的两篇文章里分别介绍了再Windows和Ubuntu下利用EMQX搭建自己的私有MQTT服务器,实现设备上云。 这篇文章介绍如何利用EMQX提供的API接口,开发用户使用的上位机(我这里分别采用python 和 Qt 进行开发测试),完成对应的功能开发。凭证获取,主题发布、消息获取…

DVWA - 2

文章目录 SQL Injectionlowmediumhigh SQL Injection low 输入 1&#xff0c;可以展示 id 1 的人员信息&#xff1a;输入 1’&#xff0c;有报错信息。可以看出是mysql数据库&#xff0c;‘‘1’’’ 去除两边的引号&#xff0c;再去除1两端的引号&#xff0c;可以看出闭合符…

linux 操作系统

先讲一下叭&#xff0c;自己学这的原因&#xff0c;是因为我在做项目的时候使用到啦Redis&#xff0c;其实在windows系统上我其实也装啦Redis上&#xff0c;但是我觉得后期在做其他的项目的时候可能也会用到这个然后就想着要不先学学redis&#xff0c;然后在后面也不至于什么都…

安卓NDK开发

1、jni&#xff1a;java native interface 作用&#xff1a;用于java代码和C、c代码的交互&#xff08;代码混编&#xff09;&#xff1b; 分类使用&#xff1a;Jni静态注册、jni动态注册 2、静态注册 1&#xff09;.绑定java方法和C/C方法的方式之一&#xff1b; …

定制 ElementPlus 主题

目录 一、安装sass二、准备定制化的样式文件三、自动导入配置 一、安装sass 基于 vite 的项目默认不支持 css 预处理器&#xff0c;需要开发者单独安装 npm i sass -D二、准备定制化的样式文件 必须在 styles/element/index.scss 文件夹下面创建 /* 只需要重写你需要的即可 */ …