多重指针变量(n重指针变量)实例分析

news2024/11/15 9:26:58

0 前言

指针之于C语言,就像子弹于枪械。没了子弹的枪械虽然可以用来肉搏,却失去了迅速解决、优雅解决战斗的能力。但上了膛的枪械也非常危险,时刻要注意是否上了保险,使用C语言的指针也是如此,要万分小心,一着不慎就可能灰飞烟灭。
对于一重指针,熟悉C语言的已经烂熟于心,但很多人对于双重指针甚至n重指针仍然抱有恐惧的心理。本文从实例出发,讲解多重指针背后的意义和使用方法。

1 n重指针介绍

1.0 什么是指针?什么是指针变量?什么是解引用?

很多人经常习惯性将指针变量说成指针,实际上指针和指针变量不是同一个东西。指针和指针变量的区别如下:

指针:内存地址
指针变量:存放内存地址的变量
解引用:*运算符获取指针(内存地址)指向(表示的)对象的值(也就是获取内存地址上存储的值,获取大小和指针类型有关)
指针和指针变量的大小都是CPU寻址的位数大小,假如使用32位MCU则大小为32位。

有关指针变量的定义和解引用及指针的解引用操作实例如下:

int main(void)
{
    int val = 0x1234;
    int *p = &val;                                    // 指针变量,初值为变量val的内存地址
    printf("*p            : 0x%x\r\n", *p);           // 解引用指针变量
    printf("*(int *)&val  : 0x%x\r\n", *(int *)&val); // 将val地址强制转换成int型指针,然后解引用
    return 0;
}

打印结果如下:
在这里插入图片描述

1.1 n重指针解引用

下面是1-4重指针的解引用,简单来说n重指针存储的是n-1重指针的地址,如果n=1(一重指针)则存储的就是对象地址:

#include "stdio.h"

int main(void)
{
    int val = 0x1234;
    int *p1 = &val;
    int **p2 = &p1;
    int ***p3 = &p2;
    int ****p4 = &p3;
    printf("****(int ****)p4 : 0x%x\r\n", ****(int ****)p4);
    printf("***(int ***)p3   : 0x%x\r\n", ***(int ***)p3);
    printf("**(int **)p2     : 0x%x\r\n", **(int **)p2);
    printf("*(int *)p1       : 0x%x\r\n", *(int *)p1);

    return 0;
}

打印结果:
在这里插入图片描述

1.2 n重指针变量的定义及解引用

下面是1-4重指针变量的解引用,简单来说n重指针变量存储的是n-1重指针变量的地址,如果n=1(一重指针变量)则存储的就是对象地址:

int main(void)
{
    int val = 0x1234;
    int *p1 = &val;
    int **p2 = &p1;
    int ***p3 = &p2;
    int ****p4 = &p3;
    printf("****p4 : 0x%x\r\n", ****p4);
    printf("***p3  : 0x%x\r\n", ***p3);
    printf("**p2   : 0x%x\r\n", **p2);
    printf("*p1    : 0x%x\r\n", *p1);

    return 0;
}

打印结果:
在这里插入图片描述
我们定义n重指针变量,可以将它拆开成2个部分看:
在这里插入图片描述
解引用的赋值也可以看成2个部分:
在这里插入图片描述

1.3 多重指针变量的用途

1.3.1 修改指针变量的值

实际上,假如我们定义多重指针变量只是为了获取对象的值,那多重指针变量相当于绕远路到达终点而不是直接使用一重指针变量直达终点。绕远路到达终点的好处在于可以修改中间指针变量的值,甚至修改我们的目的地。假如我们需要在子函数内修改父函数的指针变量的值,就可以用到双重指针,例子如下:

/**
 * @brief 将p的值修改为0x12345678
 * 
 * @param p 双重指针
 */
void set_p(int **p)
{
    *p = (int *)0x12345678;
}

int main(void)
{
    int *p1;
    set_p(&p1);
    printf("p1 val : 0x%X\r\n", p1);
}

打印结果如下:
在这里插入图片描述
说明:
我们通过&操作符取一重指针p1的内存地址传递给形参(二重指针,避免编译器警告),形参内对p1的内存地址进行一次解引用然后赋值,将p1的值修改为0x12345678。

1.3.2 避免编译器警告

对我们来说指针就是地址,n重指针存储的也是地址,那么我们为什么不可以全部使用一重指针去解引用指针呢?这主要是为了让编译器理解我们的意图,避免告警。下面的例子就会出现一个警告:

/**
 * @brief 将p的值修改为0x12345678
 * 
 * @param p 双重指针
 */
void set_p(int *p)
{
    *p = (int)0x12345678;
}

int main(void)
{
    int *p1;
    set_p(&p1);
    printf("p1 val : 0x%X\r\n", p1);
}

警告内容:
在这里插入图片描述
打印结果:
在这里插入图片描述
说明:
实际上我们通过一重指针也可以修改指针变量的值,但是编译器不知道我们的意图,向我们抛出了警告。因此,多重指针在一些场合下还可以避免警告产生。

1.4 在物理层面看多重指针的意义

指针就是内存地址,是有实际物理意义的。下面打印1-4重指针在内存上的地址,分析物理内存上多重指针解引用的过程。相关程序如下:

int main(void)
{
    int val = 0x12345678;
    int *p1 = &val;
    int **p2 = &p1;
    int ***p3 = &p2;
    int ****p4 = &p3;

    /* 地址 */
    printf("val addr : 0x%X\r\n", &val);
    printf("p1 addr  : 0x%X\r\n", &p1);
    printf("p2 addr  : 0x%X\r\n", &p2);
    printf("p3 addr  : 0x%X\r\n", &p3);
    printf("p4 addr  : 0x%X\r\n", &p4);

    /* 解引用时值变化过程 */
    printf("*p1    : 0x%x \r\n", *p1);
    printf("**p2   : 0x%x -> 0x%x\r\n", *p2, **p2);
    printf("***p3  : 0x%x -> 0x%x -> 0x%x\r\n", *p3, **p3, ***p3);
    printf("****p4 : 0x%x -> 0x%x -> 0x%x -> 0x%x\r\n", *p4, **p4, ***p4, ****p4);
}

打印结果如下:
在这里插入图片描述
注:本文使用PC运行该程序,CPU寻址位数为64位,因此指针大小为64位。
示意图如下(以p4的解引用为例):
在这里插入图片描述

2 总结

(1)指针就是内存地址,指针变量就是存储了内存地址的变量,指针的大小和CPU支持的寻址位数一致,指针解引用对象的大小和指针类型大小一致。
(2)多重指针可以作为函数形参,来实现对指针变量的修改。
(3)多重指针的解引用可以理解为绕远路获取对象的值,n重指针只有进行n次解引用才能获取到对象的值,1-n-1次解引获取到的都是指针(内存地址)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2150324.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

usemeno和usecallback区别及使用场景

1. useMemo 用途: useMemo 用于缓存计算结果。它接受一个函数和依赖项数组,只有当依赖项发生变化时,才会重新计算该函数的返回值。否则,它会返回缓存的值。 返回值: useMemo 返回的是函数执行后的结果。 使用场景: 当一个计算量大的函数在每…

Java面试篇基础部分-线程的基本方法

线程的基本方法有wait()、notify()、notifyAll()、sleep()、join()、yield()等等,这些方法都是用来控制线程的运行,并且可以实质性的影响到线程的状态变化情况。 让线程等待的方法:wait()方法 调用wait()方法的线程会进入到WAITING状态,只有等到其他线程通知或者线程被中…

【数据结构-差分】力扣1589. 所有排列中的最大和

有一个整数数组 nums ,和一个查询数组 requests ,其中 requests[i] [starti, endi] 。第 i 个查询求 nums[starti] nums[starti 1] … nums[endi - 1] nums[endi] 的结果 ,starti 和 endi 数组索引都是 从 0 开始 的。 你可以任意排列…

【Java面向对象二】static的注意事项

文章目录 前言一、关于static的三个注意点总结 前言 记录static的学习注意事项。 一、关于static的三个注意点 1、类方法中可以直接访问类的成员,不可用直接访问实例成员。 2、实例方法中既可以直接访问类成员,也可以直接访问实例成员。 3、实例方法…

105.WEB渗透测试-信息收集-FOFA语法(5)

免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动! 内容参考于: 易锦网校会员专享课 上一个内容:104.WEB渗透测试-信息收集-FOFA语法(4) 还有一个能查看信息的地方…

linux下的分布式Minio部署实践

Linux下的分布式Minio部署实践 分布式Minio部署可以将多块硬盘(位于相同机器或者不同机器)组成一个对象存储服务,避免单机环境下硬盘容量不足、单点故障等问题。 1. 简介 在当前的云计算和大数据时代,IT系统通常的设计理念都是…

英伟达Jim Fan预测:未来2~3年机器人将迎来“GPT-3时刻”

在这个科技不断进步的时代,我们终将迎来“与机器人共存”的未来。你认为,未来会是人机和平共处,还是《终结者》式未来? 随着科技发展,这个未来似乎近在咫尺。昨日外媒 The Decoder 发文报道,在最近的一次红…

Jenkins自动化部署后端项目看这篇就够了

本文主要讲解,使用Jenkins自动化部署后端工程。讲解怎么自动化部署前后的分离项目中的后端工程。 前提条件:本地需要Jenkins,如果你不知道怎么安装,可以看我的另外一篇文章。 Jenkins实现自动部署的步骤: 先拉取git…

Jboss 低版本JMX Console未授权

漏洞描述 此漏洞主要是由于JBoss中/jmx-console/HtmlAdaptor路径对外开放,并且没有任何身份验证机制,导致攻击者可以进⼊到 jmx控制台,并在其中执⾏任何功能。 影响范围 Jboss4.x以下 环境搭建 cd vulhub-master/jboss/CVE-2017-7504 d…

力扣题解2414

大家好,欢迎来到无限大的频道。 今日继续给大家带来力扣题解。 题目描述(中等): 最长的字母序连续字符串的长度 ​ ​字母序连续字符串 是由字母表中连续字母组成的字符串。换句话说,字符串 "abcdefghijklm…

linux 最简单配置免密登录

需求:两台服务器互信登录需要拉起对端服务 ip: 192.168.1.133 192.168.1.137 一、配置主机hosts,IP及主机名,两台都需要 二、192.168.1.137服务器,生成密钥 ssh-keygen -t rsa三、追加到文件 ~/.ssh/authorized_key…

分布式中间件-Pika一个高效的分布式缓存组件

文章目录 Pika简介Pika特性Pika解决的问题及应用场景Pika架构之存储引擎部署模式1、主从模式2、分布式集群模式 Pika快速上手1、二进制包方式2、源码编译方式2.1 支持的平台2.2 依赖的库软件2.3 编译过程2.4 启动 Pika2.5 清空已编译的结果2.6 Pika 的开发调试 3、容器化3.1 使…

【2025】儿童疫苗接种预约小程序(源码+文档+解答)

博主介绍: ✌我是阿龙,一名专注于Java技术领域的程序员,全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师,我在计算机毕业设计开发方面积累了丰富的经验。同时,我也是掘金、华为云、阿里云、InfoQ等平台…

【C++指南】inline内联函数详解

💓 博客主页:倔强的石头的CSDN主页 📝Gitee主页:倔强的石头的gitee主页 ⏩ 文章专栏:《C指南》 期待您的关注 目录 引言 C为什么引入了inline来替代C语言中的宏 inline的基本用法 定义inline函数 inline的优势与…

DAY20信息打点-红蓝队自动化项目资产侦察武器库部署企查产权网络空间

2.自动化-网络空间-AsamF 1.去GitHub上下载项目之后使用CMD打开 2.输入命令AsamF_windows_amd64.exe -v生成配置文件 3.AsamF会在~/.config/asamf/目录下生成config.json文件 C:\Users\Acer\.config\asamf 5.根据文档输入命令去查询所需信息(已经没有用了&#x…

C/C++通过CLion2024进行Linux远程开发保姆级教学

目前来说,对Linux远程开发支持相对比较好的也就是Clion和VSCode了,这两个其实对于C和C语言开发都很友好,大可不必过于纠结使用那个,至于VS和QtCreator,前者太过重量级了,后者更是不用说,主要用于…

解锁自动化新境界:KeymouseGo,让键盘和鼠标动起来!

文章目录 解锁自动化新境界:KeymouseGo,让键盘和鼠标动起来!背景:为何选择KeymouseGo?KeymouseGo简介安装KeymouseGo简单函数使用应用场景常见问题与解决方案总结 解锁自动化新境界:KeymouseGo,…

ISSTA 2024现场精彩:“杰出论文奖”超半数属于中国学者

ISSTA会议是软件工程领域中最具影响力的国际会议之一,也是中国计算机学会(CCF)推荐的A类会议。 第33届ISSTA会议已于奥地利维也纳圆满结束,这场盛会已经吸引了众多来自学术界和工业界的软件测试专家、研究人员和工程师&#xff0c…

学习C++的第三天!

C对C的函数部分的扩充 封装 #include <iostream> #include <memory.h> #include <stdlib.h> #include <string.h> using namespace std; using datatype int; //封装一个顺序表 class SeqList { private:datatype* ptr; //指向堆区空间的起始…

基于嵌入式的智能物流柜( 触摸屏/0.96寸oled屏)

演示 智能物流柜&#xff08;基础版&#xff09; 智能物流柜&#xff08;升级版&#xff09; 前言 这是本人在大二在学校接的一个简单的实验室项目&#xff0c;之前发布了一个&#xff0c;由于那是在暑假&#xff0c;家里器材有限&#xff0c;代码敲完之后&#xff0c;用面包板…