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)。

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

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

相关文章

状态压缩DP——AcWing 291. 蒙德里安的梦想

状态压缩DP 定义 状态压缩DP是一种利用二进制数来表示状态的动态规划算法。它通过将状态压缩成一个整数,从而减少状态数量,提高算法效率。 运用情况 状态压缩DP通常用于解决具有状态转移和最优解性质的问题,例如组合优化、图论、游戏等问…

python-邮票组合问题

[题目描述] 某人有四张3分的邮票和三张5分的邮票,用这些邮票中的一张或若干张可以得到多少种不同的邮资?输入格式: 此题无输入。输出格式: 输出可以得到不同邮资的数量。 样例输入 无样例输出 19数据范围: 对于100%的…

华为---RIP路由协议的汇总

8.3 RIP路由协议的汇总 8.3.1 原理概述 当网络中路由器的路由条目非常多时,可以通过路由汇总(又称路由汇聚或路由聚合)来减少路由条目数,加快路由收敛时间和增强网络稳定性。路由汇总的原理是,同一个自然网段内的不同子网的路由在向外(其他…

与Vatee万腾平台同行,共创智能未来

在科技日新月异的今天,智能化已成为推动社会进步的重要力量。Vatee万腾平台,作为这一浪潮中的佼佼者,正以其独特的创新力和前瞻的视野,引领我们迈向智能未来。与Vatee万腾平台同行,我们不仅能享受到科技带来的便捷与舒…

[最新教程]Claude Sonnet 3.5注册方法详细步骤分享,新手小白收藏,文末免费送已注册的Claude账号

一.Claude sonnet 3.5大模型面世 6月21日,被称为“OpenAI 最强竞对”的大模型公司 Anthropic 发布了 Claude 3.5 系列模型中的第一个版本——Claude 3.5 Sonnet。 Anthropic 在官方博客中表示,Claude 3.5 Sonnet 提高了智能化的行业标准,在…

传统图像特征描述及提取方法

目录 一、图像特征描述 二、图像特征的分类 2.1 图像的点、线、面特征 2.2 图像的纹理形状特征 2.3 图像颜色特征 2.4图像的统计特征 三、图像特征提取的评价 一、图像特征描述 图像特征是一幅图像区别于另一幅图像最基本的特征,是其可以作为标志性的属性。 图像特征分为…

2024 年解锁 Android 手机的 7 种简便方法

您是否忘记了 Android 手机的 Android 锁屏密码,并且您的手机已被锁定?您需要使用锁屏解锁 Android 手机?别担心,您不是唯一一个忘记密码的人。我将向您展示如何解锁 Android 手机的锁屏。 密码 PIN 可保护您的 Android 手机和 G…

高考志愿填报,如何权衡学校和专业?

高考是人生的分水岭,成绩好的学生能就读更好的大学,获得更多的学习资源,但也有一些同学即使凭借高分数进入了高校,专业的学习过程却不尽如人意,他们也没有将100%的精力投入到专业学习当中。 无论高考结束之后获得了多…

python--fasApi学习(Dash+FastAPI框架)

在学习fastApi 框架时,发现了一个好用的框架,参考: 博客参考: https://blog.csdn.net/gitblog_00002/article/details/137331157下载文档并部署: 下载代码: git clone https://gitee.com/insistence2022/…

LeetCode 1-两数之和

LeetCode第1题 两数之和 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现…

全流程FVCOM水环境、污染物迁移、水交换、水质、潮流、温盐、波浪及泥沙数值模拟

近年来,随着计算技术的发展和对海洋、水环境问题认识的加深,数值模拟技术在海洋、水环境等科学研究中的应用越来越广泛。FVCOM因其独特的优点,成为研究海洋动力过程、污染物扩散、水质变化等问题的重要工具。作为一种基于有限体积法的数值模型…

DBeaver 数据结果集设置不显示逗号(太丑了)

从Navicat切换过来使用DBeaver,发现类似bigint 这种数据类型在结果集窗口中显示总是给我加上一个逗号,看着很不习惯,也比较占空间,个人觉得这种可读性也不好。 于是我在网上尝试搜索设置方法,可能我的关键词没命中&…

利用LabVIEW和机器学习实现无规律物体识别

针对变化无规律的物体识别,LabVIEW结合机器学习算法提供了一种高效的解决方案。介绍如何使用LabVIEW编程实现此功能,包括所需工具包、算法选择和实现步骤,帮助开发者在无规律的复杂环境中实现高精度的物体识别。 1. 项目概述 无规律物体的识…

【D3.js in Action 3 精译】关于本书

文章目录 本书读者本书结构与路线图本书代码liveBook 在线论坛 D3.js 项目的传统开发步骤 本书读者 这本书适用于所有渴望在数据可视化工作中获得完全创意自由的人,从定制化的经典图表到创建独特的数据可视化布局,涵盖内容广泛,应有尽有。您…

centos中安装并设置vsftpd

vsftpd是一个可安装在linux上的ftp服务器软件。 一、安装 安装前保证服务器能上互联网。如果不能上网,看看能不能设法利用局域网代理上网。 sudo yum -y install vsftpd二、配置 1、修改配置文件 cd /etc/vsftpd #修改之前记得备份!!&am…

Elasticsearch中的post_filter后置过滤器技术

Hi~!这里是奋斗的小羊,很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~~ 💥💥个人主页:奋斗的小羊 💥💥所属专栏:C语言 🚀本系列文章为个人学习…

「iOS」UI——无限轮播图实现与UIPageControl运用

「OC」UI 文章目录 「OC」UI无限轮播图的实现以及UIPageControl的实际运用明确要求简单滚动视图的实现UIPageControl的实现设置NSTimer实现自动移动补充实现 进行无限滚动视图的修改思路实现 完整代码展示 无限轮播图的实现以及UIPageControl的实际运用 明确要求 我们要实现一…

计算机组成原理笔记-第1章 计算机系统概论

第一章 计算机系统概论 笔记PDF版本已上传至Github个人仓库:CourseNotes,欢迎fork和star,拥抱开源,一起完善。 1.1 计算机系统简介 计算机系统组成: 硬件:计算机的实体,如主机、外设等软件&am…

ffmpeg windows系统详细教程

视频做预览时黑屏,但有声音问题解决方案。 需要将 .mp4编成H.264格式的.mp4 一般上传视频的站点,如YouTube、Vimeo 等,通常会在用户上传视频时自动对视频进行转码,以确保视频能够在各种设备和网络条件下流畅播放。这些网站通常…

媒体邀约有啥要注意的

传媒如春雨,润物细无声,大家好,我是51媒体网胡老师。 媒体宣传加速季,100万补贴享不停,一手媒体资源,全国100城线下落地执行。详情请联系胡老师。 媒体邀约是邀请媒体参与活动或报道的重要过程&#xff0c…