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

指针可以指向一份普通类型的数据,例如 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.mfbz.cn/a/129675.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

github遇到想要强制拉取远程仓库内容

进行项目的时候&#xff0c;遇到了我的远程仓库 Sync fork 更新以后&#xff0c;这时候我的本地就和远程不同步&#xff0c;如果使用 git pull 的时候&#xff0c;如果出现 conficts 过多的情况怎么办&#xff0c;如果我们想要直接把远程仓库拉下来应该怎么办&#xff1f; git…

srs webrtc推拉流环境搭建

官方代码https://github.com/ossrs/srs 拉取代码&#xff1a; git clone https://github.com/ossrs/srs.gitcd ./configure make ./objs/srs -c conf/rtc.confconf/rtc.conf中&#xff0c;当推拉流浏览器在本地时&#xff0c;如果srs也在本地&#xff0c;那么可以使用官网默认…

​怎么测试websocket接口

在部分业务中&#xff0c;我们需要使用长连接&#xff0c;我们可以使用http长连接或者websocket&#xff0c;开发结束后难免会遇到测试问题&#xff0c;这里推荐2个&#xff0c;一个是postman&#xff0c;一个是网站 postman 测试网站 测这边推荐测试网站&#xff0c;支持ws/w…

Python开源项目CodeFormer——人脸重建(Face Restoration),模糊清晰、划痕修复及黑白上色的实践

无论是自己、家人或是朋友、客户的照片&#xff0c;免不了有些是黑白的、被污损的、模糊的&#xff0c;总想着修复一下。作为一个程序员 或者 程序员的家属&#xff0c;当然都有责任满足他们的需求、实现他们的想法。除了这个&#xff0c;学习了本文的成果&#xff0c;或许你还…

如何记录血压的波动情况

import pandas as pd from plotnine import * import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [Microsoft YaHei] 记录时间(time)、收缩压(SBP)、舒张压(DBP)&#xff1a; df pd.DataFrame({ time: [2023-11-01 08:30, 2023-11-02 21:00, 2023-11-0…

C++之旅(学习笔记)第6章 基本操作

C之旅&#xff08;学习笔记&#xff09;第6章 基本操作 6.1 基本操作 class X{ public:X(Sometype); // "普通的构造函数": 创建一个对象X(); // 默认构造函数X(const X&); // 拷贝构造函数X(X&&); // 移动构造函数X& operator(const …

规划文献阅读——Obstacle Avoidance, Path Planning and Control for Autonomous Vehicles

摘要 自动驾驶汽车避障需要三个主要层面&#xff0c;即感知、路径规划和制导控制。在本文中&#xff0c;考虑到这三个层次之间的联系&#xff0c;提出了一个全局架构。在环境感知层面&#xff0c;采用基于证据占用网格的方法进行动态障碍物检测。因此&#xff0c;考虑物体的姿…

鸿蒙原生应用开发-DevEco Studio远程模拟器的使用

使用单设备模拟器运行应用/服务 Remote Emulator支持Phone、Wearable、Tablet、TV等设备类型&#xff0c;但不同区域&#xff08;开发者帐号注册地&#xff09;支持的设备类型可能不同&#xff0c;请以实际可申请的设备类型为准。 Remote Emulator中的单设备模拟器&#xff08…

Sprint Boot 学习路线 3

嵌入式服务器 Spring Boot 的嵌入式服务器功能是一项方便而强大的功能&#xff0c;它允许你在应用程序中直接运行 Web 服务器&#xff0c;无需将其部署到单独的独立 Web 服务器中。这使得开发、测试和部署 Web 应用程序变得容易&#xff0c;而且它还是轻量级的、易于启动和停止…

linuxC语言缓冲区及小程序的实现

文章目录 1.文件缓冲区1.1介绍1.2缓冲文件系统1.3冲刷函数fflush1.4认识linux下的缓冲区 2.linux小程序的实现2.1 回车\r和换行\n2.2倒计时程序2.3进度条小程序sleep/usleep代码运行结果 1.文件缓冲区 1.1介绍 为缓和 CPU 与 I/O 设备之间速度不匹配&#xff0c;文件缓冲区用以…

【Maven教程】(十):使用 Hudson 进行持续集成—— 从Hudson的安装到任务创建 ~

Maven 使用 Hudson 进行持续集成 1️⃣ 持续集成的作用、过程和优势2️⃣ Hudson 简介与安装3️⃣ 准备 Subversion 仓库4️⃣ Hudson 的基本系统设置5️⃣ 创建 Hudson 任务5.1 Hudson 任务的基本配置5.2 Hudson 任务的源码仓库配置5.3 Hudson 任务的构建触发配置5.4 Hudson …

python之SPC:计算Cpk

目录 1、Ca、Cp和Cpk的理解 2、python计算Cp,Cpk与Pp,Ppk 3、总结 1、Ca、Cp和Cpk的理解 Ca、Cp和Cpk是制程能力指数&#xff0c;它们分别代表制程准确度、制程精密度和制程能力指数。 制程准确度&#xff08;Ca&#xff09;反映实际平均值与规格中心值之一致性。对于单边…

GF0-57CQD-002 测量参数:加速度、速度、位移–现场可配置

GF0-57CQD-002 测量参数:加速度、速度、位移–现场可配置 GF0-57CQD-002 是一款创新的双通道变送器&#xff0c;专为精确的振动测量而设计。它激励并读取来自加速度计的信号&#xff0c;并将整体振动值作为电流/电压信号传输。它测量加速度、速度和位移等不同参数的振动。配置…

竞赛 车道线检测(自动驾驶 机器视觉)

0 前言 无人驾驶技术是机器学习为主的一门前沿领域&#xff0c;在无人驾驶领域中机器学习的各种算法随处可见&#xff0c;今天学长给大家介绍无人驾驶技术中的车道线检测。 1 车道线检测 在无人驾驶领域每一个任务都是相当复杂&#xff0c;看上去无从下手。那么面对这样极其…

Vxe table - 基于Vue的宝藏级 table 组件

文章目录 前言一、Vxe-table功能点计划 二&#xff0c;安装三&#xff0c;引入四&#xff0c;示例用法 前言 对于表格来说&#xff0c;也许我们会遇到一个需求就是表格中的单元格可编辑&#xff0c;如果我们使用的是ElementUI也许不太好办&#xff0c;因为官方没有可编辑的这个…

Spring封装数据结果

Spring封装数据结果 POST请求JSON格式 基本数据类型 public class Demo {private byte aByte;private short aShort;private int anInt;private long aLong;private float aFloat;private double aDouble;private char aChar;private boolean aBoolean; }没有传键 封装时就会…

【Spring】SpringBoot配置文件

SpringBoot配置文件 配置文件作用SpringBoot配置文件配置文件快速入手配置文件的格式properties配置文件说明基本语法读取配置文件properties缺点分析 yml配置文件说明yml基本语法yml使用进阶yml配置读取配置对象配置集合配置Mapyml优缺点 配置文件作用 计算机上有数以千计的配…

Unity 一些内置宏定义

在Unity中&#xff0c;有一些内置的宏定义可用于不同的平台。以下是一些常见的平台内置宏定义&#xff1a; 1、UNITY_EDITOR&#xff1a;在Unity编辑器中运行。 2、UNITY_EDITOR_WIN&#xff1a;在Unity编辑器运行在Windows操作系统时被定义。 3、UNITY_STANDALONE&#xff1a…

Linux学习第37天:Linux I2C 驱动实验(一):哥俩好

Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 世界上的很多事物都是成双成对出现的。也包括在驱动开发的过程中&#xff0c;比如I2C中其实就是数据线和时钟线的相互配合才能完成的。 I2C常用于连接各种外设、…

Ubuntu 22.04 安装水星无线 USB 网卡

我的 USB 网卡是水星 Mercury 的&#xff0c; 在 Ubuntu 22.04 下面没有自动识别。 没有无线网卡的时候只能用有线接到路由器上&#xff0c;非常不方便。 寻思着把无线网卡驱动装好。折腾了几个小时装好了驱动。 1.检查网卡类型 & 安装驱动 使用 lsusb 看到的不一定是准确…
最新文章