当前位置: 首页 > news >正文

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的三次方。 程序分析&#xff…...

【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平衡旋转&#xff09…...

第一百一十八节 Java面向对象设计 - Java接口

Java面向对象设计 - Java接口 什么是接口? Java中的接口定义了一个引用类型来创建抽象概念。接口由类实现以提供概念的实现。 在Java 8之前,一个接口只能包含抽象方法。 Java 8允许接口具有实现的静态和默认方法。 接口通过抽象概念定义不相关类之间…...

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

超短脉冲激光自聚焦效应

前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...

LeetCode - 394. 字符串解码

题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...

三体问题详解

从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...

CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云

目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

Rapidio门铃消息FIFO溢出机制

关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

dify打造数据可视化图表

一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...

AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机

这个博客介绍了如何通过 settings.json 文件添加一个无人机外的 固定位置监控相机,因为在使用过程中发现 Airsim 对外部监控相机的描述模糊,而 Cosys-Airsim 在官方文档中没有提供外部监控相机设置,最后在源码示例中找到了,所以感…...