深入理解C/C++指针:从基本操作到复杂表达式

       

目录

代码展示:

示例1:指向数组结束位置之后的地址

示例2:结构体大小对指针运算的影响

示例3:访问数组元素的不同方式

示例4:逗号表达式在数组初始化中的应用

示例5:计算多维数组元素间的地址差值

示例6和7:指针与二维数组及字符串数组的操作

示例8:复杂指针表达式与字符串输出


指针是一个强大且灵活的工具,它们允许我们以低级视角直接操作内存。本文将通过一系列实例,深入解析指针如何与数组、结构体以及字符串相互作用,并演示一些复杂的指针表达式。

代码展示:

// 指针操作与数组、结构体相关示例// 示例1:指针指向数组结束位置之后的地址
int main() {int a[5] = { 1, 2, 3, 4, 5 };int* ptr = (int*)((&a + 1)); // 指针ptr指向数组a最后一个元素之后的位置printf("%d,%d\n", *(a + 1), *(ptr - 1)); // 输出数组第二个元素和最后一个元素的值return 0;
}// 解释:
// 在C/C++中,`&a`获取数组a的起始地址。由于数组在内存中是连续存储的,加1后相当于指向数组结尾后的一个位置。
// `*(a + 1)` 访问数组a的第二个元素(索引为1)。
// `*(ptr - 1)` 回溯到数组a的最后一个元素的地址并获取其值。// 示例2:结构体Test大小为20字节时,不同类型的指针运算结果
struct Test {int Num;         // 通常占用4个字节char* pcName;    // 指针类型在32位系统上占用4个字节short sDate;     // short 类型占用2个字节char cha[2];     // 字符数组 cha 占用2个字节short sBa[4];    // 短整型数组 sBa 占用8个字节
}*p = (struct Test*)0x10000;int main() {printf("%p\n", (p + 0x1)); // 结果:0x100014 -- 结构体指针+1相当于增加一个结构体大小(20字节)printf("%p\n", (unsigned long)p + 0x1); // 结果:0x100001 -- 转换成无符号长整数后直接数字加1printf("%p\n", (unsigned int*)p + 0x1); // 结果:0x100004 -- 转换成无符号整数指针后,指针加1相当于增加4/8字节return 0;
}// 示例3:访问数组元素的不同方式
int main()
{int a[4] = { 1,2,3,4 };int* ptr1 = (int*)(&a + 1);//4 --  (ptr1[-1] == *(ptr1-1)int* ptr2 = (int*)((int)a + 1);//0x200000 -- 0x00 000005 -> 5 + 1 = 6 -> 0x00 00 00 06printf("0x%x\n0x%x", ptr1[-1], *ptr2);return 0;
}// 示例4:逗号表达式在初始化多维数组中的应用
int main() {int a[3][2] = { (0,1),(2,3),(4,5) }; // 使用逗号表达式分别初始化每一行的元素int* p = a[0];printf("%d", p[0]); // 输出:1return 0;
}// 示例5:计算多维数组元素之间的地址差值
int main()
{int a[5][5];//int(*)[5] -- 首元素int(*p)[4];//int(*)[4]  p = a;  //将a的地址赋值给p//指针-指针取中间元素的个数printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]); //p[4][2] == *(*(p+4)+2)return 0;		
}// 示例6:指向二维数组的指针操作
int main() {int aa[2][5] = { 1,2,3,4,5 , 6,7,8,9,10 };int* ptr1 = (int*)(&aa + 1);int* ptr2 = (int*)(*(aa + 1));printf("%d,%d\n", *(ptr1 - 1), *(ptr2 - 1)); // 输出:10, 5return 0;
}// 示例7:指向字符数组的指针递增和字符串输出
int main() {char* a[] = { "Work","at","alibaba" };char** pa = a;pa++;printf("%s\n", *pa); // 输出:"at"// 示例8:复杂指针表达式与字符串输出char* c[] = { "ENTER","NEW","POINT","FIRST" };char** cp[] = { c + 3,c + 2,c + 1,c };char*** cpp = cp;printf("%s\n", **++cpp); // 输出:"POINT"printf("%s\n", *-- * ++cpp + 3); // 输出:"ER"printf("%s\n", *cpp[-2] + 3); // 输出:"st"printf("%s\n", cpp[-1][-1] + 1); // 输出:"EW"return 0;
}

示例1:指向数组结束位置之后的地址

int main() {int a[5] = { 1, 2, 3, 4, 5 };int* ptr = (int*)((&a + 1)); // 指针ptr指向数组a最后一个元素之后的位置printf("%d,%d\n", *(a + 1), *(ptr - 1)); // 输出数组第二个和最后一个元素的值
}

在这个示例中,我们首先创建了一个包含5个整数的数组a。然后,我们将一个指向整型的指针ptr初始化为数组a的结束位置之后的一个地址。由于数组是连续存储的,因此*(ptr - 1)能够访问数组的最后一个元素。同时,*(a + 1)访问数组的第二个元素。

示例2:结构体大小对指针运算的影响

struct Test {// ...
}*p = (struct Test*)0x10000;int main() {printf("%p\n", (p + 0x1)); // 结果反映了结构体大小// ...
}

此示例展示了结构体类型指针在进行算术运算时,加1实际上意味着增加结构体所占用的字节数。在本例中,根据Test结构体的定义,其大小为20字节,因此(p + 0x1)的结果会增加相应的字节数。

示例3:访问数组元素的不同方式

int main() {int a[4] = { 1,2,3,4 };int* ptr1 = (int*)(&a + 1);int* ptr2 = (int*)((int)a + 1);printf("0x%x,0x%x\n", ptr1[-1], *ptr2);
}

这里我们展示了两种不同的方法来计算数组结束后的地址,并回溯至数组最后一个元素。这有助于理解指针如何基于数组首地址和类型大小进行移动。

示例4:逗号表达式在数组初始化中的应用

int main() {int a[3][2] = { (0,1),(2,3),(4,5) };// ...
}

通过逗号表达式可以分别初始化多维数组的每一行元素。例如,在这个2D数组初始化中,每个括号内的逗号表达式都用于设置该行的两个元素。

示例5:计算多维数组元素间的地址差值

int main() {int a[5][5];// ...printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
}

示例6和7:指针与二维数组及字符串数组的操作

在这两个示例中,我们进一步探讨了指针如何遍历和访问二维数组及字符串数组的元素。通过对指针进行递增或利用复杂的指针表达式,我们可以实现对数组深层次元素的灵活访问和操作。

// 示例6:指向二维数组的指针操作
int main() {int aa[2][5] = { 1,2,3,4,5 , 6,7,8,9,10 };int* ptr1 = (int*)(&aa + 1);int* ptr2 = (int*)(*(aa + 1));printf("%d,%d\n", *(ptr1 - 1), *(ptr2 - 1)); // 输出:10, 5return 0;
}// 示例7:指向字符数组的指针递增和字符串输出
int main() {char* a[] = { "Work","at","alibaba" };char** pa = a;pa++;printf("%s\n", *pa); // 输出:"at"

示例8:复杂指针表达式与字符串输出

在最后一个示例中,我们进一步展示了如何利用多级指针和复杂的指针表达式来访问和操作字符数组(即字符串)。例如:

//cpp-1和cpp--(一个会改变cpp的位置,但是cpp-1不会改变cpp当前的指向,只是返回了一个对应的值)
int main()
{char* c[] = { "ENTER","NEW","POINT","FIRST" };char** cp[] = { c + 3,c + 2,c + 1,c };char*** cpp = cp;printf("%s\n", **++cpp);//POINT -- c+2printf("%s\n", *-- * ++cpp + 3);//ER -- c+2 => c+1printf("%s\n", *cpp[-2] + 3);//st -- **(cpp-2)+2printf("%s\n", cpp[-1][-1] + 1);//EW  -- *(*(cpp-1)-1 )+1return 0;
}

在这个例子中,我们首先创建了一个包含多个字符串的字符指针数组c。然后定义了二级指针数组cp,其元素是c数组中的不同起始位置。最后,通过三级指针cpp指向cp数组。

  • 第一条语句将三级指针cpp递增后解引用两次,得到的是cp[1]所指向的字符串,即"POINT"。
  • 第二条语句先递增cpp并解引用一次,然后对结果进行自减操作再解引用,接着加上偏移量3,得到的是"POINT"字符串从第四个字符开始的部分,即"ER"。
  • 第三条语句直接使用cpp[-2]获取到cp数组中倒数第二个元素,并在其基础上加3,因此输出为"NEW"字符串从第三个字符开始的部分,即"st"。
  • 最后一条语句通过cpp[-1][-1]获取到cp数组中最后一个元素所指向的字符串"FIRST"的最后一个字符'F',然后加1,输出为"EW"。

        通过这些复杂的指针表达式示例,我们可以看到指针在C/C++中能够实现灵活且深入的数据访问和操作,但同时也要求开发者对其有深刻的理解以避免潜在的错误和陷阱。在实际编程中,正确合理地使用指针可以大大提高代码效率,同时也能让程序逻辑更为清晰和紧凑。

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

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

相关文章

【Python】不一样的Ansible(一)

不一样的Ansible——进阶学习 前言正文概念Ansible CorePlugins和Modules 插件插件类型编写自定义插件基本要求插件选项文档标准编写插件 添加一个本地插件注册为内置插件指定插件目录 其他一些技巧更改Strategy 结语 前言 Ansible 是一个极其简单的 IT 自动化引擎&#xff0c…

【Unity】动态申请权限

1、AndroidManifest.xml在<application></application>内添加一行&#xff1a; <meta-data android:name"unityplayer.SkipPermissionsDialog" android:value"true" /> 作用&#xff1a;屏蔽应用启动时弹出申请权限弹窗&#xff08;危…

签到积分--题解--c++--模拟

题目描述 每天签到一个应用程序&#xff0c;至少可以得 11 分&#xff0c;若前一天也签到了&#xff0c;则当天得分为前一天的得分加 11&#xff0c;一天最多只能得 55 分。 给定一个由 Y 及 N 构成的字符序列&#xff0c;该序列表示小爱每天签到的情况&#xff0c;签到记为 …

Windows下Redis5+可视化软件下载、安装和配置教程-2024年1月8日

Windows下Redis5下载、安装和配置教程-2024年1月8日 一、下载二、安装三、配置环境四、配置可视化客户端 一、下载 redis是现在是没有对win系统版进行维护的&#xff0c;这个是大神完成的&#xff0c;目前是到5版本&#xff0c;选择Redis-x64-5.0.14.1.zip点击下载 下载地址&…

pgAdmin和asdf postgres的安装

安装pgAdmin&#xff1a; curl https://www.pgadmin.org/static/packages_pgadmin_org.pub | sudo apt-key addsudo sh -c echo "deb https://ftp.postgresql.org/pub/pgadmin/pgadmin4/apt/$(lsb_release -cs) pgadmin4 main" > /etc/apt/sources.list.d/pgadmi…

Linux CentOS官方文档之U盘安装

准本U盘安装媒介 https://docs.centos.org/en-US/centos/install-guide/Making_Media_USB/ 在Mac OS X上制作安装USB媒介 1. 查看U盘 $ diskutil list2. umountdisk $ diskutil unmountDisk /dev/disknumber Unmount of all volumes on disknumber was successful3. 使用d…

.NET 6中如何使用Redis

1、安装redis Redis在windows平台上不受官方支持&#xff0c;所以想要在window安装Redis就必须去下载windows提供的安装包。安装地址&#xff1a;https://github.com/tporadowski/redis/releases 2、在NueGet安装包 3、在appsettings.json文件里面添加Redis相关配置信息 &quo…

MySQL之子查询、连接查询(内外)以及分页查询

一、案例&#xff08;接上一篇文章&#xff09; 09&#xff09;查询学过「张三」老师授课的同学的信息 -- 一共有两种方式 -- 第一种方式&#xff1a; SELECT s.*,c.cname,t.tname,sc.score FROMt_mysql_teacher t,t_mysql_course c,t_mysql_student s,t_mysql_score sc WHERE…

鸿蒙设备-开发板基础学习(BearPi-HM Micro)

theme: minimalism 每当学习一门新的编程语言或者上手一款新的开发板&#xff0c;在学习鸿蒙设备开发过程中&#xff0c;带大家写的第一个程序&#xff0c;通过这个程序&#xff0c;我们可以对鸿蒙设备开发的整个流程有一个初步的体验。BearPi-HM Micro开发板为例&#xff1a;…

「MCU」SD NAND芯片之国产新选择优秀

文章目录 前言 传统SD卡和可贴片SD卡 传统SD卡 可贴片SD卡 实际使用 总结 前言 随着目前时代的快速发展&#xff0c;即使是使用MCU的项目上也经常有大数据存储的需求。可以看到经常有小伙伴这样提问&#xff1a; 大家好&#xff0c;请问有没有SD卡芯片&#xff0c;可以…

MongoDB高级集群架构设计

两地三中心集群架构设计 容灾级别 RPO & RTO RPO&#xff08;Recovery Point Objective&#xff09;&#xff1a;即数据恢复点目标&#xff0c;主要指的是业务系统所能容忍的数据丢失量。RTO&#xff08;Recovery Time Objective&#xff09;&#xff1a;即恢复时间目标&…

ARM CCA机密计算架构软件栈(上)

一、简介 该博客描述了Arm机密计算架构(Arm CCA)的固件和软件组件。 在这个博客中,将学到如何: 列出组成Arm CCA软件栈的组件集了解Arm CCA引入新软件组件的原因了解Monitor和领域管理监视器(RMM)的角色了解如何创建和管理领域1.1 开始之前 假设熟悉AArch64异常模型、…

C++学习笔记——string类和new函数

目录 string类 1.功能增强 1.1 子字符串提取 1.2 字符串拼接 1.3 大小写转换 1.4 字符串比较 2.性能优化 3.使用示例 下面是一个简单的使用示例&#xff0c;展示了如何使用改进后的String类&#xff1a; NEW函数 2.1NEW函数的基本用法 2.2NEW函数的注意事项 2.3避…

密码学:一文读懂非对称加密算法 DH、RSA

文章目录 前言非对称加密算法的由来非对称加密算法的家谱1.基于因子分解难题2.基于离散对数难题 密钥交换算法-DH密钥交换算法-DH的通信模型初始化DH算法密钥对甲方构建DH算法本地密钥乙方构建DH算法本地密钥DH算法加密消息传递 典型非对称加密算法-RSARSA的通信模型RSA特有的的…

Golang 通道之 select 语句

前言 select 语句是一个强大的特性&#xff0c;用于同时等待多个通道操作。select 可以监听多个通道的读写事件&#xff0c;并在有通道准备好时执行相关的case。如果有多个case同时就绪&#xff0c;select 会随机选择一个执行。如果没有case就绪&#xff0c;select 将阻塞&…

建模软件Rhinoceros mac介绍说明

Rhinoceros mac是一款3D设计软件“犀牛”&#xff0c;在当今众多三维建模软件中&#xff0c;Rhinoceros 版因为其体积小、功能强大、对硬件要求低而广受欢迎&#xff0c;对于专业的3D设计人员来说它是一款不错的3D建模软件&#xff0c;Rhinoceros Mac中文版能轻易整合3DS MAX与…

编程语言的走向又将如何呢?

编程语言的未来&#xff1f; 随着科技的飞速发展&#xff0c;编程语言在计算机领域中扮演着至关重要的角色。它们是软件开发的核心&#xff0c;为程序员提供了与机器沟通的桥梁。那么&#xff0c;在技术不断进步的未来&#xff0c;编程语言的走向又将如何呢&#xff1f; 1. 更…

Git命令+github仓库克隆

Git github Git常用命令 开始 git init #创建仓库 git status #查看仓库的状态 git status -s #简单的查看仓库的状态 git ls-files #查看暂存区的内容 git reflog #查看操作的历史记录 暂存区 git add git add <file&g…

网安入门11-文件上传(前后端绕过,变形马图片马)

Upload-Labs Upload-Labs是一个使用PHP语言编写、专注于文件上传漏洞的闯关式网络安全靶场。练习该靶场可以有效地了解并掌握文件上传漏洞的原理、利用方法和修复方案。 思考&#xff1a;他只让我传一个.jpg的图片&#xff0c;我想传一个.php的木马&#xff0c;两者什么区别 …

如何计算指标波动贡献率?(附Pandas实现)

大家好&#xff0c;我是阿粥 “为什么这个月销售额提升了30%&#xff1f;” “为什么转化率又降了&#xff0c;同比竟然降低了42%&#xff0c;什么原因导致的呢&#xff1f;” 这些都是数据分析师在工作中经常会遇到的问题&#xff0c;甚至有些基础岗的数据分析师要花80%以上的…