C语言入门系列:指针入门(超详细)
文章目录
- 一,什么是指针
- 1,内存
- 2,指针是什么?
- 二,指针的声明
- 1,声明指针类型变量
- 2,二级指针
- 三,指针的计算
- 1,两个指针运算符
- 1.1 *运算符
- 1.2 & 运算符
- 1.3 &运算符与*运算符的关系
- 2,指针变量的初始化
- 2.1 指针变量的大坑
- 2.2 指针变量初始化
- 2.3 指针变量初始化最佳实践
- 三,指针的运算
- 1,指针与整数值的加减运算
- 2,指针与指针的加法运算
- 3,指针与指针的减法
- 4,指针与指针的比较运算
指针是 C 语言的重点,也是难点,这篇文章主要讲解指针是什么以及如何使用。
一,什么是指针
1,内存
指针与内存是息息相关的,在学习指针之前,先回忆下内存相关的知识。

内存是程序运行期间存储数据的硬件设备,为了方便管理,计算机将内存划分为一个个小的单元,每个单元的大小是一个字节。
如果把内存比作一栋酒店大楼,内存单元就像是一个个小的房间,数据就住在小房间里。

我们知道,为了客人能准确找到属于自己的房间,酒店房间是有房号的。

同样,内存单元也是有编号的,这个编号在计算机中称之为“内存地址”。

2,指针是什么?
指针就是内存单元的编号,本质上是一个内存地址,相当于房卡上的房间号。
从形式上看,指针和整型数据并没有什么区别,都是数字。区别在于,指针是内存地址,不用于与其他数据进行加减乘除等运算,也不会展示给用户。
二,指针的声明
1,声明指针类型变量
在编写代码过程中,通常会声明一个变量,然后对变量进行赋值或者其他各种运算。
要使用指针,也需要声明一个指针类型的变量。
一定要牢记:指针变量就是一个普通变量,只不过它的值是内存地址而已。
指针类型由两部分构成:
- ①指针标识符,C语言用字符
*表示指针 - ②指针类型,指针不能单独存在,必须和数据类型一起出现,表明这个指针是某种数据类型的指针
比如,char*表示一个指向字符的指针,float*表示一个指向float类型的值的指针。
int* intPtr;
上面示例声明了一个变量intPtr,它是一个指针,指向的内存地址存放的是一个整数。
星号*可以放在变量名与类型关键字之间的任何地方,下面的写法都是正确的。
int *intPtr;
int * intPtr;
int* intPtr;
推荐使用星号紧跟在类型关键字后面的写法,即int* intPtr;。
声明指针变量时需要注意,如果要在一行声明多个指针变量,每个变量前都要携带字符*。
// 正确
int * foo, * bar;// 错误
int* foo, bar;
上面示例中,第二行实际上仅仅声明了一个指针变量,foo是整数指针变量,而bar是整数变量,即*只对第一个变量生效。
2,二级指针
一个指针指向的可能还是指针,这时就要用两个星号**表示,这种指针通常称为二级指针。
int** foo;
上面示例表示变量foo是一个指针,即变量foo存储的还是一个内存地址,这个内存地址指向的内存中存储的则是一个整数。
int a = 10;
// &a表示a变量的内存地址
int* pa = &a;
// &a表示指针变量pa的内存地址
int** ppa = &pa;

三,指针的计算
1,两个指针运算符
1.1 *运算符
*这个字符除了声明变量时代表指针外,还可以作为运算符,用来获取指针指向的内存中的值。
void plus1(int* p) {*p = *p + 1;
}
上面代码中,函数plus1的参数是一个整数指针p。
函数体里面,*p就表示指针p所指向的那个整数值。
对*p赋值,就是改变指针p指向的内存中的值。
这有点绕,和普通变量对比更容易理解。
int a = 10;
int b = 100;
// 将b的地址赋于指针pb
int* pb = &b;*pb = *pb +1;
a = a + 1;
对于上述代码的最后两行:
*pb = *pb +1,这个表达式可以拆解为4步,①计算机首先从指针变量pb中取出地址0xffeecc,②再去这个地址指向的内存单元中获取整数100,③然后执行运算100+1,执行完成后,④0xffeecc这个内存单元的值就变成101。a = a + 1,相当于上面的表达式,执行过程更简单。①计算机从变量a对应的内存直接取出整数10,②然后执行运算10+1,③执行完成后,a变量对应的内存的数据更新为11。

1.2 & 运算符
&运算符用来取出一个变量所在的内存地址。
int x = 1;
printf("x's address is %p\n", &x);
上面示例中,x是一个整数变量,&x就是x的值所在的内存地址。printf()的%p是内存地址的占位符,可以打印出内存地址。
上一小节中,参数变量加1的函数,可以像下面这样使用。
void plus1(int* p) {*p = *p + 1;
}int x = 1;
plus1(&x);
printf("%d\n", x); // 2
注意,调用plus1()函数以后,打印变量x的值,发现结果是2,但是我们并没有对x进行显示的重新赋值,原因调用plus1函数时,将变量c的地址作为参数进行传递,plus1直接根据地址取出初始值,执行加1的运算,然后更新内存中的值为2,不必使用变量x就可以修改x变量的值。
1.3 &运算符与*运算符的关系
&运算符与*运算符互为逆运算,下面的表达式,是成立的。
int i = 5;if (i == *(&i)) // 正确
2,指针变量的初始化
2.1 指针变量的大坑
声明指针变量之后,编译器会为指针变量本身分配一个内存空间,这个内存空间可能还保存着历史数据。
也就是说,这个指针变量可能指向一个随机的地址。
如果此时就去读写这个地址对应的内存,可能出现非常严重的后果,必然这个地址指向的是账户余额,有可能导致账户虚增或者虚减。
int* p;
*p = 1; // 错误
上述代码是必须避免的,因为指针p指向的内存单元是随机的。
2.2 指针变量初始化
正确写法是声明指针变量声明,立即指向一个明确的地址,这就是指针变量的初始化,初始化之后再进行读写。
int* p;
int i;p = &i;
*p = 13;
上面示例中,p是指针变量,声明这个变量后,p会指向一个随机的内存地址。
这时要将它指向一个已经分配好的内存地址,上例就是再声明一个整数变量i,编译器会为i分配内存地址,然后让p指向i的内存地址(p = &i;)。
完成初始化之后,就可以对p指向的内存地址进行赋值了(*p = 13;)。
2.3 指针变量初始化最佳实践
强烈推荐,声明指针变量的同时,将指针变量的值设为NULL。
int* p = NULL;
NULL在 C 语言中是一个常量,表示地址为0的内存空间,这个地址是无法使用的,读写该地址会报错。
这样即使之后我们忘记了把指针变量p指向预期的内存地址,在程序运行过程中会报错,而不是以可怕的、随机的方式运行。
三,指针的运算
我们现在知道了,指针虽然代表的是内存地址,但其本质上是一个无符号整数。
C语言允许指针参与运算,但是指针的运算规则和整数的运算规则是相差很大的。
1,指针与整数值的加减运算
指针与整数值的运算,表示指针的移动。
short* j;
j = (short*)0x1234;
j = j + 1; // 0x1236
上面示例中,j是一个指针,指向内存地址0x1234。
由于0x1234本身是整数类型(int),跟j的类型(short*)并不兼容,所以强制使用类型投射,将0x1234转成short*。
表明上看,j + 1应该等于0x1235,但正确答案是0x1236。
原因是j + 1表示指针向内存地址的高位移动一个单位,而一个单位的short类型占据两个字节的宽度,所以相当于向高位移动两个字节。同样的,j - 1得到的结果是0x1232。
指针移动的单位,与指针指向的数据类型有关。数据类型占据多少个字节,每单位就移动多少个字节。
2,指针与指针的加法运算
指针只能与整数值进行加减运算,两个指针进行加法是非法的。
unsigned short* j;
unsigned short* k;
x = j + k; // 非法
上面示例是两个指针相加,这是非法的。
3,指针与指针的减法
相同类型的指针允许进行减法运算,返回它们之间的距离,即相隔多少个数据单位。
高位地址减去低位地址,返回的是正值;低位地址减去高位地址,返回的是负值。
这时,减法返回的值属于ptrdiff_t类型,这是一个带符号的整数类型别名,具体类型根据系统不同而不同。这个类型的原型定义在头文件stddef.h里面。
short* j1;
short* j2;j1 = (short*)0x1234;
j2 = (short*)0x1236;ptrdiff_t dist = j2 - j1;
printf("%td\n", dist); // 1
上面示例中,j1和j2是两个指向 short 类型的指针,变量dist是它们之间的距离,类型为ptrdiff_t,值为1,因为相差2个字节正好存放一个 short 类型的值。
4,指针与指针的比较运算
指针之间的比较运算,比较的是各自的内存地址哪一个更大,返回值是整数1(true)或0(false)。
相关文章:
C语言入门系列:指针入门(超详细)
文章目录 一,什么是指针1,内存2,指针是什么? 二,指针的声明1,声明指针类型变量2,二级指针 三,指针的计算1,两个指针运算符1.1 *运算符1.2 & 运算符1.3 &运算符与…...
打印水仙花数
题目:打印出所有的“水仙花数”,所谓“水仙花数”是指一个三位数,其各位数字立方和等于该数本身。 例如:153是一个“水仙花数”,因为153 1的三次方 +5的三次方+3的三次方。 程序分析ÿ…...
【SCAU数据挖掘】数据挖掘期末总复习题库简答题及解析——下
1.从某超市顾客中随机抽取5名,他们的购物篮数据的二元0/1表示如下: 顾客号 面包 牛奶 尿布 啤酒 鸡蛋 可乐 1 1 1 0 0 0 0 2 1 0 1 1 1 0 3 0 1 1 1 0 1 4 1 1 1 1 0 0 5 1 1 1 0 0 1 某学生依据这些数据做…...
PyQt学习之简介
1.Python图形界面称为程序的用户交互界面,英文称之为 UI (user interface) Tkinter 基于Tk的Python库,Python官方采用的标准库,优点是作为Python标准库、稳定、发布程序较小,缺点是控件相对较少。 wxPython 基于wxWidgets的Py…...
深入理解前端缓存
前端缓存是所有前端程序员在成长历程中必须要面临的问题,它会让我们的项目得到非常大的优化提升,同样也会带来一些其它方面的困扰。大部分前端程序员也了解一些缓存相关的知识,比如:强缓存、协商缓存、cookie等,但是我…...
K-means聚类算法详解与实战
一、引言 K-means聚类算法是一种无监督学习算法,旨在将数据点划分为K个不同的聚类或群组,使得同一聚类内的数据点尽可能相似,而不同聚类间的数据点尽可能不同。在图像处理、数据挖掘、客户细分等领域有着广泛的应用。本文将通过图文结合的方…...
python数据分析-糖尿病数据集数据分析预测
一、研究背景和意义 糖尿病是美国最普遍的慢性病之一,每年影响数百万美国人,并对经济造成重大的经济负担。糖尿病是一种严重的慢性疾病,其中个体失去有效调节血液中葡萄糖水平的能力,并可能导致生活质量和预期寿命下降。。。。 …...
【前端】 nvm安装管理多版本node、 npm install失败解决方式
【问题】If you believe this might be a permissions issue, please double-check the npm ERR! permissio或者Error: EPERM: operation not permitted, VScode中npm install或cnpm install报错 简单总结,我们运行npm install 无法安装吧包,提示权限问题…...
第11天:API开发与REST framework
第11天:API开发与REST framework 目标 使用Django REST framework构建RESTful API。 任务概览 学习序列化器的概念和使用方法。创建API视图和路由。实现API的权限和认证。 详细步骤 1. 学习序列化器 序列化器是Django REST framework中用于数据转换的组件&am…...
2024 年解锁 Android 手机的 7 种简便方法
您是否忘记了 Android 手机的 Android 锁屏密码,并且您的手机已被锁定?您需要使用锁屏解锁 Android 手机?别担心,您不是唯一一个忘记密码的人。我将向您展示如何解锁 Android 手机的锁屏。 密码 PIN 可保护您的 Android 手机和 G…...
利用机器学习重构视频中的人脸
引言 中国与英国的研究团队携手合作,开创了一种创新的视频面孔重塑技术。这项技术能够以极高的一致性对视频中的面部结构进行逼真的放大和缩小,且避免了常见伪影的产生。 从研究人员选取的YouTube视频样例中可见,经过处理后,女演…...
2021数学建模C题目– 生产企业原材料的订购与运输
C 题——生产企业原材料的订购与运输 思路:该题主要是通过对供应商的供货能力和运送商的运货能力进行估计,给出合适的材料订购方案 程序获取 第一题问题思路与结果: 对 402 家供应商的供货特征进行量化分析,建立反映保障企业生…...
C# OpenCvSharp 图像复制-clone、copyTo
在C#中使用OpenCvSharp库处理图像时,clone和copyTo是两个非常常用的函数。理解和合理使用这些函数可以帮助你在图像处理项目中更高效地操作图像数据。本文将详细介绍这两个函数的使用方法,并通过具体的示例来说明它们的实际应用。 1. clone 函数 定义 …...
中国投入到终止遗传性疾病的战斗
中国投入到终止遗传性疾病的战斗 编译 李升伟 于2006年6月在澳大利亚的墨尔本会议上启动的人类变异组计划(Human Variome Project,简称HVP),旨在全球范围内广泛收集所有基因和蛋白质序列变异和多态性的数据,采用全基…...
PCL common中常见的基础功能函数
文章目录 一、common模块中的头文件二、common模块中的基本函数1、angles.h2、centroid.h1)计算给定一群点的3D中心点,并且返回一个三维向量2)计算给定的三维点云的协方差矩阵。3)计算正则化的3*3的协方差矩阵以及给定点云数据的中心点4)利用一组点的指数对其进行一般的、…...
力扣每日一题 6/22 字符串/贪心
博客主页:誓则盟约系列专栏:IT竞赛 专栏关注博主,后期持续更新系列文章如果有错误感谢请大家批评指出,及时修改感谢大家点赞👍收藏⭐评论✍ 2663.字典序最小的美丽字符串【困难】 题目: 如果一个字符串满…...
MCT Self-Refine:创新集成蒙特卡洛树搜索 (MCTS)提高复杂数学推理任务的性能,超GPT4,使用 LLaMa-3 8B 进行自我优化
📜 文献卡 题目: Accessing GPT-4 level Mathematical Olympiad Solutions via Monte Carlo Tree Self-refine with LLaMa-3 8B作者: Di Zhang; Xiaoshui Huang; Dongzhan Zhou; Yuqiang Li; Wanli OuyangDOI: 10.48550/arXiv.2406.07394摘要: This pape…...
自制HTML5游戏《开心消消乐》
1. 引言 游戏介绍 《开心消消乐》是一款基于HTML5技术开发的网页游戏,以其简单的操作方式、轻松的游戏体验和高度的互动性,迅速在社交平台上获得了广泛的关注和传播。玩家通过消除相同类型的元素来获得分数,游戏设计巧妙,易于上手…...
【C++】平衡二叉树(AVL树)的实现
目录 一、AVL树的概念二、AVL树的实现1、AVL树的定义2. 平衡二叉树的插入2.1 按照二叉排序树的方式插入并更新平衡因子2.2 AVL树的旋转2.2.1 新节点插入较高左子树的左侧(LL平衡旋转)2.2.2 新节点插入较高右子树的右侧(RR平衡旋转)…...
第一百一十八节 Java面向对象设计 - Java接口
Java面向对象设计 - Java接口 什么是接口? Java中的接口定义了一个引用类型来创建抽象概念。接口由类实现以提供概念的实现。 在Java 8之前,一个接口只能包含抽象方法。 Java 8允许接口具有实现的静态和默认方法。 接口通过抽象概念定义不相关类之间…...
浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)
✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义(Task Definition&…...
网络六边形受到攻击
大家读完觉得有帮助记得关注和点赞!!! 抽象 现代智能交通系统 (ITS) 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 (…...
Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...
Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...
人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...
音视频——I2S 协议详解
I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议,专门用于在数字音频设备之间传输数字音频数据。它由飞利浦(Philips)公司开发,以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...
三分算法与DeepSeek辅助证明是单峰函数
前置 单峰函数有唯一的最大值,最大值左侧的数值严格单调递增,最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值,最小值左侧的数值严格单调递减,最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...
MySQL:分区的基本使用
目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区(Partitioning)是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分(分区)可以独立存储、管理和优化,…...
