C语言之指针初阶


目录

前言

一、内存与地址的关系

二、指针变量

三、野指针

四、const

五、传值调用与传址调用

总结


前言

        本文主要介绍C语言指针的一些基础知识,为后面深入理解指针打下基础,因此本文内容主要包括内存与地址的关系,指针的基本语法,指针运算,野指针,还有const修饰指针和assert断言的使用,最后还会讲到指针的传址调用,希望对大家有所帮助。


一、内存与地址的关系

指针作为C语言的核心知识,那么指针究竟是什么呢?

  1. 首先指针其实就是地址,而地址是内存中一个个内存单元的编号
  2. 我们知道计算机上CPU(中央处理器)在处理数据的时候,需要的数据是在内存中读取的,处理后的数据也会放回内存中,那我们买电脑的时候,电脑上内存有8GB/16GB/32GB等,这些内存就是程序运行时需要用到的内存
  3. 为了更高效的管理与使用这些内存,于是就将这些内存分为一个个内存单元,每个内存单元的大小取一个字节,也就是8个比特位,⼀个比特位可以存储一个2进制的位1或者0
  4. 每个内存单元也都有一个编号,有了这个内存单元的编号,CPU就可以快速找到一个内存空间,在计算机中我们把内存单元的编号也称为地址。C语言中给地址起了新的名字:指针
  5. 所以我们可以这样理解:内存单元的编号 == 地址 == 指针

如图:


二、指针变量

指针变量就是储存地址的变量

1. 取地址操作符  &

&操作符是一个单目操作符,用来取出一个变量的地址

比如,创建 int a,观察其地址

图中画红线的部分就为a变量在内存中的地址以及储存的数据,即0x004FF82C为地址,0a 00 00 00为以16进制储存的数据,其中 0a 表示的就是以16进制保存的十进制数字10,因为一个16进制数需要4个比特位表示,0a就是两个16进制数,占了8个比特位刚好为一个字节,而0a 00 00 00 四个这样的就刚好表示4个字节,至于为什么0a在前面,这是编译器自己的规则


2.指针变量创建

对于一般的指针变量的创建

(类型) * 变量名;

这样就将变量 a ,b 的地址存储在对应类型的指针变量里面,其余的如float、double等可以以此类推

注意:这里的*号表示其变量为指针变量


3.解引用操作符  *

* 解引用操作符为一个单目操作符,它可以通过地址找到其对应的数据

因为指针变量存储的就是地址,所以指针变量搭配*就可以找到其对应的数据进行操作

如:

我们可以通过解引用操作符修改指针对应的变量数据


4.指针变量类型的意义

指针变量的大小和类型无关,只要是指针变量,在同一个平台下,大小都是一样的,比如在32位平台上指针类型的变量大小都是4个字节,64位平台上为8个字节(以下在32位上演示)

因此,指针变量的类型有什么意义呢?

其实,这个意义非常重要:指针的类型决定了,对指针解引用的时候有多大的权限(一次能操作几个字节)。比如: char* 的指针解引用就只能访问⼀个字节,而 int* 的指针的解引用就能访问四个字节

如:我们再次创建一个变量a。(注:程序每次运行时分配的地址不一样)

除了a变量的地址不一样,其他和上面一样为 0a 00 00 00,它表示的是10,并且每两位表示一个字节,而一个字节表示一个内存单元,因此如上的0x0099FC8C其实表示的是储存0a的地址,我们可以一列一列的观察其内存

因为a为整形变量,占4个字节,因此其在内存中为4个连续的内存单元,如上标记的区域,此时如果我们创建一个整形指针变量接收a的地址,那么我们解引用该指针就可以操作这四个字节

如:

注:90 01 00 00 在读取时是以 00000190,也就是190,为16进制

十进制刚好为400

此时变量a可以被正常修改,而如果我们以字符类型的指针接收a的地址后,我们一次只能修改一个字节

如:

16进制28等于十进制40,如上我们貌似也能正常修改整形变量a的数组,但实际上只要我们修改的数值大于两位16进制能表达的最大数字,就不能正常修改a的数值

如:

我们只能改变一个字节,也就是char类型的指针一次只能修改一个字节

这就是指针类型的意义,当然不止如此,指针变量的类型还决定了指针加减整数的时候一次跳过多少字节,下面就会讲到


5.指针 + - 整数

先说结论:指针加减整数,会使指针前进或后退n个字节,而指针的类型决定了指针向前或者向后走一步有多大(距离)

也就是说,指针类型决定了指针加减1时的步长,比如char*指针,它一次只能跳过一个字节,它加减n也就是向前或向后跳过n*sizeof(char)个字节

比如:

(注:此处不能int *parr = arr不能写成&arr,下篇指针进阶文章我会讲到) 

此处我们就利用了循环来使数组首元素地址依次跳过 i个int类型大小的字节,实现了循环打印数组元素

此处我们有几处需要注意的点:

  1. 数组的元素在内存中是连续存放的,并且地址由低到高,不了解的可以参考我主页的数组文章
  2. 此处我们发现,如果把 *(arr+i) 换成 arr[ i ] 也就是我们之前的写法达到的效果是一样的,这是因为 *(arr+i) 是完全等价于 arr[ i ] ,也就是说,当编译器遇到 arr[ i ] 时会把它解读为 *(arr+i),按这样理解,因为 arr+i 等于 i+arr ,也就是可以写成 *(i+arr),进而可以写成 i[arr] ,我们可以验证一下
  3. 答案是完全可以的,但是平时不建议这样写因为可读性不如 arr[ i ]。总结就是 [ ] 操作符其实也是解引用的效果,只不过多了加法的作用


6.指针 - 指针

对于指针 - 指针这个运算来说,只有两指针指向的是同一块连续的内存区域时才有意义

我们可以通过指针 - 指针来计算数组两元素地址之间有多少个元素

如:

那么为什么是9个而不是10个呢?

我们可以画图来理解:

画图我们就可以很直观的感受到,arr[9] 的元素没有被计算到


三、野指针

1.概念

野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)。

野指针成因:

  1. 指针未初始化:主要是创建在函数中的指针变量没有进行初始化,造成指针指向的地址是随机值,此时指针指向的地址随机,不能对其进行解引用。
  2. 指针越界访问:这种主要出现在数组中,指针指向的地址超出了数组所在的内存区域,指向了一个不确定的地址
  3. 指针指向的空间被释放:这种主要发生在指针变量指向的地址是已经被释放的内存空间地址,被释放的空间不属于该程序,虽然可能引用不会导致报错,但是不安全


2.如何规避野指针

野指针的危害有:访问违规、数据损坏、内存泄露、安全风险等。

野指针的危害众多、因此我们的代码中需要规避野指针,那么如何规避野指针呢?

  1. 指针变量初始化时如果没有需要赋值的地址就先赋值为NULL
  2. 指针变量不再使用时,及时置NULL,指针使用之前检查有效性:当指针变量指向一块区域的时候,我们可以通过指针访问该区域,后期不再使用这个指针访问空间的时候,我们可以把该指针置为NULL。因为约定俗成的⼀个规则就是:只要是NULL指针就不去访问, 同时使用指针之前可以判断指针是否为NULL。
  3. 避免返回局部变量的地址

除了以上的方法,还有一个常用的方法:

assert 断言

assert.h 头文件定义了宏 assert() ,用于在运行时确保程序符合指定条件,如果不符合,就报错终止运行。这个宏常常被称为“断言”

比如:assert (p != NULL);

上面代码在程序运行到这一行语句时,验证变量 p 是否等于 NULL 。如果确实不等于NULL 继续运行,否则就会终止运行,并且给出报错信息提示。

程序 assert() 宏接受⼀个表达式作为参数。如果该表达式为真(返回值非零), assert()不会产生任何作用,程序继续运行。如果该表达式为假(返回值为零), assert() 就会报错,在标准错误流 stderr 中写入一条错误信息,显示没有通过的表达式,以及包含这个表达式的文件名和行号。

如:

assert() 的使用对程序员是非常友好的,使用 assert() 有几个好处:它不仅能自动标识文件和出问题的行号,还有一种无需更改代码就能开启或关闭 assert() 的机制。如果已经确认程序没有问 题,不需要再做断言,就在 #include <assert.h>语句的前面,定义一个宏 NDEBUG

如下assert()就会失去作用:

如果想再次启用assert()只需要注释掉第一行的宏就行

assert() 的缺点:因为引入了额外的检查,增加了程序的运行时间。 一般我们可以在 Debug 中使用,在 Release 版本中选择禁用 assert 就行,在 VS 这样的集成开发环境中,Release 版本中,assert()直接就是自动优化掉了。这样在debug版本写有利于程序员排查问题,在Release 版本不影响用户使用时程序的效率。


四、const

const的作用:被const修饰的变量不能被直接修改

如:

程序在还未运行时已经发出错误警告

虽然不能直接修改,但是还能通过指针变量间接修改:


但是如果const修饰的是指针变量,就分以下两种情况:

  • const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。 但是指针变量本身的内容可变。
  • const如果放在*的右边,修饰的是指针变量本身,保证了指针变量的内容不能修改,但是指针指向的内容,可以通过指针改变。

如:


五、传值调用与传址调用

传值调用与传址调用,这个主要针对的是自定义函数的参数,也就是说:

  1. 函数参数为非指针类型,调用时传入非指针类型参数,即为传值调用
  2. 函数参数为指针类型,调用时传给函数地址,即为传址调用

那么这两个有什么区别呢?

其实主要是传值调用时,在函数内部修改形参不会影响实参,而在传址调用时,修改形参也同样会会修改实参

比如这个例题:编写一个函数,交换两个整形变量的内容

此前我们在主函数中只需要再创建一个变量,通过三者交换即可达成题目这样的效果,但如果我们在自定义函数里面,函数参数为两个整形变量,分别接收需要交换内容的两个实参,使用一样的方法是达不到一样的效果的,这时候我们只需要使用传址调用即可

如:

通过传给函数实参的地址,在函数中用指针变量的形参接收,就可以在函数中解引用该指针变量来修改对应的实参变量的内容

这就是传值调用与传址调用的不同

另外,如果函数的参数为数组类型,其实也是指针变量,给函数传参时,一般传入的就是数组名,因为数组名就是数组首元素的地址,至于详细原因我会在指针进阶中讲到


总结

        以上就是本文的全部内容,希望对大家有所帮助,下一篇我会继续写指针的进阶篇,感谢大家的支持

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

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

相关文章

高精度原理介绍及代码实现

目录 高精度 引入 使用场景 实现原理 高精度加法 数据存储 加法实现 总代码 高精度减法 与加法的不同点&#xff1a; 总代码 高精度乘法 总代码 高精度除法 总结 总注意点 减法注意点 高精度 引入 所谓高精度并不是很高级难懂的东西&#xff0c;只是对传统的…

使用 CloudFlare 后如何才能不影响搜索引擎蜘蛛爬虫

今天,明月给大家再次详细讲解一下,明月在使用 CloudFlare 后如何才能不影响搜索引擎蜘蛛爬虫对站点的抓取,因为这是很多首次使用 CloudFlare 的站长们容易忽略和触犯的问题,并不是 CloudFlare 不友好,而是 CloudFlare 的防火墙(WAF)实在是太给力。其实在【CloudFlare 如…

SQLZOO:The JOIN operation

数据表&#xff1a;game-gaol-eteam game idmdatestadiumteam1team210018 June 2012National Stadium, WarsawPOLGRE10028 June 2012Stadion Miejski (Wroclaw)RUSCZE100312 June 2012Stadion Miejski (Wroclaw)GRECZE100412 June 2012National Stadium, WarsawPOLRUS... goal …

每日OJ题_贪心算法四④_力扣397. 整数替换

目录 力扣397. 整数替换 解析代码 力扣397. 整数替换 397. 整数替换 难度 中等 给定一个正整数 n &#xff0c;你可以做如下操作&#xff1a; 如果 n 是偶数&#xff0c;则用 n / 2替换 n 。如果 n 是奇数&#xff0c;则可以用 n 1或n - 1替换 n 。 返回 n 变为 1 所需…

最大子矩阵:前缀和、动态规划

最近在学习动态规划&#xff0c;在牛客上刷题时碰到了这一题。其实最初的想法是暴力和前缀和&#xff0c;但是时间复杂度极高&#xff0c;需要套4层循环。后来去网上搜了一下相关的题解和做法&#xff0c;进而了解到了前缀和&#xff0b;线性动态规划的做法。但是在成功做出这题…

排除对象属性序列化的三种方式

说明&#xff1a;在项目里&#xff0c;经常可以看到以下日志内容&#xff0c;将对象序列化后直接打印出来&#xff0c;观察对象数据&#xff0c;判断当前处理逻辑正确与否。 &#xff08;以下信息来自&#xff1a;https://www.tl.beer/randbankcard.html生成器&#xff0c;信息…

python跟C++选哪个?

选择使用Python还是C取决于你的具体需求和项目背景。我这里有一套编程入门教程&#xff0c;不仅包含了详细的视频讲解&#xff0c;项目实战。如果你渴望学习编程&#xff0c;不妨点个关注&#xff0c;给个评论222&#xff0c;私信22&#xff0c;我在后台发给你。 在通信工程行业…

第六节笔记及作业----Lagent AgentLego 智能体应用搭建

关于 Agent 的相关理论 大语言模型存在一些局限性&#xff0c;比如会出现幻觉问题、有时效性问题以及可靠性问题。智能体的定义是具备感知、决策和行动能力的实体。智能体主要由感知部分、大脑部分和动作部分组成。智能体有多种类型&#xff0c;如 ReAct 类型&#xff08;侧重…

TCP服务器实现将客服端发送的信息广播发送(使用内核链表管理客户端信息)

目录 1.服务器端实现思路 2.服务器端代码 3.客户端代码 4.内核链表代码 5.运行格式 一、服务器端 二、客户端 6.效果 1.服务器端实现思路 Tcp广播服务初始化 等待客户端连接 广播发送 2.服务器端代码 #include "list.h" #include <signal.h> #def…

视频打赏系统源码

地球号&#xff1a;xiaobao0214520(WX) 支付对接&#xff0c;盒子推广&#xff0c;域名防封&#xff0c;等一系列功能皆为正常&#xff0c;后台账号密码:地球号&#xff1a;xiaobao0214520(WX)&#xff0c;测试网站,可以定制哦支付对接&#xff0c;盒子推广&#xff0c;域名防…

免费思维13招之七:空间型思维

免费思维13招之七:空间型思维 本篇给你带来的是空间型思维。 空间型思维,具体分为内部空间型思维和外部空间型思维。 什么叫内部空间型思维呢? 内部空间型就是充分利用现有空间或资源为社会提供免费服务,积累人气,增加流量,从而带动消费。 为什么你生意不好?为什么你…

python数据分析——matplotlib可视化基础

参考资料&#xff1a;活用pandas库 # 导入库 import pandas as pd import matplotlib.pyplot as plt # 导入数据 anscombepd.read_csv(r"...\seaborn常用数据案例\anscombe.csv") anscombe.head() 大多数基本图表的名字以plt.plot开头。 # 创建数据子集 # 只包含数…

图片转word如何转换?

要将图片转换为Word文档&#xff0c;你可以使用以下方法之一&#xff1a; 以上这些方法都可以帮助你将图片中的文本转换为可编辑的Word文档&#xff0c;你可以根据自己的喜好和需求选择其中一种方法来操作。 使用OCR软件或在线工具&#xff1a;有许多OCR&#xff08;Optical Ch…

【Spring】验证 @ServerEndpoint 的类成员变量线程安全

文章目录 前言猜想来源验证方法Controller 的情况ServerEndpoint 的情况 后记 前言 最近有 websocket 的需求。探索 ServerEndpoint 的类成员变量特点。 这里类比 Controller 讨论 ServerEndpoint 类成员变量是否线程安全。 猜想来源 网上的教程大多数都这么展示程序&#…

Ardupilot开源代码之Rover上路 - 后续1

Ardupilot开源代码之Rover上路 - 后续1 1. 源由2. 问题汇总2.1 问题1&#xff1a;飞控选择2.2 问题2&#xff1a;飞控安装位置和固定2.3 问题3&#xff1a;各种插头、插座配套2.4 问题4&#xff1a;分电板缺陷2.5 问题5&#xff1a;电机编码器接线及正反向问题2.6 问题6&#x…

树莓派python开发

树莓派自带thonny 点亮LED灯 import RPi.GPIO as GPIO import time# 设置GPIO模式为BCM GPIO.setmode(GPIO.BCM)# 设置LED引脚 led_pin 18# 设置LED引脚为输出 GPIO.setup(led_pin, GPIO.OUT)# 点亮LED GPIO.output(led_pin, GPIO.HIGH)# 延时2秒 time.sleep(2)# 关闭LED GPI…

电商核心技术揭秘55:社群与粉丝经济的结合

相关系列文章 电商技术揭秘相关系列文章合集&#xff08;1&#xff09; 电商技术揭秘相关系列文章合集&#xff08;2&#xff09; 电商技术揭秘相关系列文章合集&#xff08;3&#xff09; 电商技术揭秘四十一&#xff1a;电商平台的营销系统浅析 电商技术揭秘四十二&#…

Jboss 反序列化 CVE-2017-12149

一、漏洞简介 JBoss是一个管理EJB的容器和服务器&#xff0c;支持EJB 1.1、EJB 2.0和EJB3的规范。在/invoker/readonly路径下&#xff0c;攻击者可以构造序列化代码传入服务器进行反序列化,由于没有对反序列化操作进行任何检测&#xff0c;导致攻击者可以执行任意代码。 而jbo…

重发布和路由策略实验(课堂练习)

需求&#xff1a; 将1.1.1.0/24网段&#xff08;不在OSPF中&#xff09;重发布到网络中&#xff0c;不允许出现次优路径&#xff0c;实现全网可达。 需求分析&#xff1a; 1、在R1上重发布1.1.1.0/24网段&#xff0c;但是需要过滤192.168.12.0/24和192.168.13.0/24 2、在R2和R3…

考研数学|强化阶段怎么刷《660》《880》《1000》?

强化阶段想要刷好题&#xff0c;首先要选一本适合自己的题集&#xff01; 一般在强化阶段&#xff0c;大家用多个最多的题集就是660题&#xff0c;880题还有1000题 660题的特点是只训练客观题&#xff0c;虽然题目的质量很高&#xff0c;但是训练面还是比较窄 880题是综合训…