C语言学习:速通指针(2)

这里要学习的有以下内容

1. const修饰指针

2. 野指针

3. assert断⾔

4. 指针的使⽤和传址调⽤

那么从这里开始

1. const 修饰指针

const修饰变量

        首先我们知道变量是可以修改的,如果把变量的地址交给⼀个指针变量,通过指针变量的也可以修改这个变量。 但是如果我们不希望这个变量被修改,那我们该怎么做呢?

那么在这里就可以使用const。const的作用就是保持变量不被更改。

 #include int main()
{int m = 0;m = 20;//m是可以修改的 const int n = 0; n = 20;//n是不能被修改的 return 0; 
} 

        上述代码中n是不能被修改的,其实n本质是变量,只不过被const修饰后,在语法上加了限制,只要我 们在代码中对n进⾏修改,就不符合语法规则,就报错,致使没法直接修改n。

那么在这里我害怕不小心将n修改所以给n加上const,但是我又需要对n进行修改那么给n加上const就一定写死无法更改了吗?

        当然不是,这时我们可以绕过n,使⽤n的地址,去修改n就能做到了,虽然这样做是在打破语法规则。

#include int main() 
{ const int n = 0;printf("n = %d\n", n); int* p = &n; *p = 20; printf("n = %d\n", n); return 0; 
} 

输出结果:

        程序运⾏结果 我们可以看到这⾥⼀个确实修改了,但是我们还是要思考⼀下,为什么n要被const修饰呢?就是为了 不能被修改,如果p拿到n的地址就能修改n,这样就打破了const的限制,这是不合理的,所以应该让 p拿到n的地址也不能修改n,那接下来怎么做呢?

        const 修饰指针变量 ⼀般来讲const修饰指针变量,可以放在*的左边,也可以放在*的右边,意义是不⼀样的。 int * p;//没有const修饰?

        int const * p;//const 放在*的左边做修饰

        int * const p;//const 放在*的右边做修饰

        我们看下⾯代码,来分析具体分析⼀下:

#include<stdio.h>//代码1 - 测试⽆const修饰的情况 
void test1() 
{ int n = 10; int m = 20; int* p = &n; *p = 20;//可更改 p = &m; //可更改
} 
//代码2 - 测试const放在*的左边情况     
void test2() 
{ int n = 10; int m = 20; const int* p = &n; *p = 20;//不可更改p = &m; //可更改 
} 
//代码3 - 测试const放在*的右边情况 
void test3() 
{ int n = 10; int m = 20; int * const p = &n; *p = 20; //可更改p = &m; //不可更改
}//代码4 - 测试*的左右两边都有const 
void test4() 
{ int n = 10; int m = 20; int const * const p = &n; *p = 20; //不可更改 p = &m; //不可更改 
}int main() 
{ //测试⽆const修饰的情况 test1();     //测试const放在*的左边情况 test2(); //测试const放在*的右边情况 test3(); //测试*的左右两边都有const test4(); return 0; 
} 

         结论:const修饰指针变量的时候

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

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

2. 野指针

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

        2.1 野指针成因

        1. 指针未初始化

#include <stdio.h>int main()
{ int *p;//局部变量指针未初始化,默认为随机值 ,这时p就为野指针*p = 20; return 0; 
} 

        2. 指针越界访问        

#include <stdio.h>
int main()
{int arr[10] = {0};int *p = &arr[0];int i = 0;for(i = 0; i <= 11; i++)
{//当指针指向的范围超出数组arr的范围时,p就是野指针*(p++) = i;
}return 0;
}

2.2 如何规避野指针

        2.2.1 指针初始化

        如果明确知道指针指向哪⾥就直接赋值地址,如果不知道指针应该指向哪⾥,可以给指针赋值NULL. NULL 是C语⾔中定义的⼀个标识符常量,值是0,0也是地址,这个地址是⽆法使⽤的,读写该地址 会报错。

        初始化如下:

 #include <stdio.h>
int main() 
{ int num = 10; int*p1 =  &num;int*p2 = NULL; return 0; 
} 

        2.2.2 ⼩⼼指针越界 

        ⼀个程序向内存申请了哪些空间,通过指针也就只能访问哪些空间,不能超出范围访问,超出了就是 越界访问,也就成为了野指针

        2.2.3 将指针置为空

        在指针使用结束后要将指针置为NULL,避免他对代码以后的使用的过程中造成影响。

int main()
{int arr[10] = {1,2,3,4,5,6,7,8,9,10};int *p = &arr[0];int i = 0;for(i=0; i<10; i++)
{*(p++) = i;
}//此时p已经越界了,可以把p置为NULLp = NULL;//下次使⽤的时候,判断p不为NULL的时候再使⽤//...p = &arr[0];//重新让p获得地址if(p != NULL) //判断{//...}return 0;
}

        2.2.4 避免返回局部变量的地址

        如造成野指针的第3个例⼦,不要返回局部变量的地址。

3. assert 断⾔

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

 assert(p != NULL);

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

        assert() 宏接受⼀个表达式作为参数。

        如果该表达式为真(返回值⾮零), assert() 不会产⽣ 任何作⽤,程序继续运⾏。

        如果该表达式为假(返回值为零), assert() 就会报错,在标准错误 流 stderr 中写⼊⼀条错误信息,显⽰没有通过的表达式,以及包含这个表达式的⽂件名和⾏号。

        使⽤ assert() 有⼏个好处:它不仅能⾃动标识⽂件和 出问题的⾏号,还有⼀种⽆需更改代码就能开启或关闭 assert() 的机制。

        当我们已经确认程序没有问题,不需要再做断⾔,只需要在 #include 语句的前⾯,定义⼀个宏 NDEBUG 就可以了,无需再进行其他操作。

#define NDEBUG
#include <assert.h>

        然后,重新编译程序,编译器就会禁⽤⽂件中所有的 assert() 语句。

        当程序⼜出现问题,只需要移除这条 #define NDEBUG 指令(或者把它注释掉),再次编译,这样就重新启⽤了 assert() 语句。

        然而assert也是有缺点的

        assert() 的缺点是,因为引⼊了额外的检查,增加了程序的运⾏时间。

        为了解决这个问题,⼀般我们可以在 Debug 中使⽤,在 Release 版本中选择禁⽤ assert 就⾏,

        在 VS 这样的集成开发环境的 Release 版本中,assert就直接被优化掉了。这样在debug版本写有利于程序员排查问题, 在 Release 版本不影响⽤⼾使⽤时程序的效率。

        4. 指针的使⽤和传址调⽤

        4.1 传值调⽤和传址调⽤

学习指针的⽬的是使⽤指针解决问题,那什么问题,⾮指针不可呢?

例如:写⼀个函数,交换两个整型变量的值 ⼀番思考后,我们可能写出这样的代码:

#include <stdio.h>
void Swap1(int x, int y)
{int tmp = x;x = y;y = tmp;
}
int main()
{int a = 0;int b = 0;scanf("%d %d", &a, &b);printf("交换前:a=%d b=%d\n", a, b);Swap1(a, b);printf("交换后:a=%d b=%d\n", a, b);return 0;
}

        我们发现在main函数内部,创建了a和b,(这里假设地址) :a的地址是0x00cffdd0,b的地址是0x00cffdc4。在调⽤ Swap1函数时,将a和b传递给了Swap1函数,在Swap1函数内部创建了形参x和y接收a和b的值,但是 x的地址是0x00cffcec,y的地址是0x00cffcf0,x和y确实接收到了a和b的值,不过x的地址和a的地址不 ⼀样,y的地址和b的地址不⼀样,相当于x和y是独⽴的空间,那么在Swap1函数内部交换x和y的值, ⾃然不会影响a和b,当Swap1函数调⽤结束后回到main函数,a和b实际上没有交换。Swap1函数在使⽤ 的时候,是把变量本⾝直接传递给了函数,这种调⽤函数的⽅式我们之前在函数的时候就知道了,这 种叫传值调⽤。

        结论:实参传递给形参的时候,形参会单独创建⼀份临时空间来接收实参,对形参的修改不影响实 参。

在这里可以使用传址调用

#include <stdio.h>
void Swap2(int*px, int*py)
{int tmp = 0;tmp = *px;*px = *py;*py = tmp;
}
int main()
{int a = 0;int b = 0;scanf("%d %d", &a, &b);printf("交换前:a=%d b=%d\n", a, b);Swap2(&a, &b);printf("交换后:a=%d b=%d\n", a, b);return 0;
}

我们可以看到实现成Swap2的⽅式,顺利完成了任务,这⾥调⽤Swap2函数的时候是将变量的地址传 递给了函数,这样就直接改变了原地址处的数据。

这种函数调⽤⽅式叫:传址调⽤。

在这里可以使用这个方法来实现strlen函数的实现这里可以参看以前的博客:C语言学习:速通字符串函数-CSDN博客文章浏览阅读880次,点赞68次,收藏14次。在编程的过程中我们不免会对字符穿进行操作,那么我们就会经常使用字符串函数。那么这里我就给分享四个字符串函数。https://blog.csdn.net/2402_87907999/article/details/143661229?fromshare=blogdetail&sharetype=blogdetail&sharerId=143661229&sharerefer=PC&sharesource=2402_87907999&sharefrom=from_link

  感谢观看,有兴趣的话,点赞,收藏,关注,来一波吧。制作过程中如有错误希望可以慷慨指出。最后有更多想法可以一起在评论区聊一聊,私信我也可以哦

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

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

相关文章

使用 MATLAB 绘制三维散点图:根据坐标和距离映射点的颜色和大小

在数据可视化中&#xff0c;三维散点图是一种非常直观的方式来展示数据的分布。MATLAB 提供了强大的 scatter3 函数&#xff0c;可以用来绘制三维散点图&#xff0c;而通过调整点的颜色和大小&#xff0c;可以进一步增强图形的表现力。 在本篇博客中&#xff0c;我们将逐步讲解…

卷积神经网络(CNN)的层次结构

卷积神经网络&#xff08;CNN&#xff09;是一种以其处理图像和视频数据的能力而闻名的深度学习模型&#xff0c;其基本结构通常包括以下几个层次&#xff0c;每个层次都有其特定的功能和作用&#xff1a; 1. 输入层&#xff08;Input Layer&#xff09;&#xff1a; 卷积神经网…

【深度学习】四大图像分类网络之ResNet

ResNet网络是在2015年由微软实验室中的何凯明等几位提出&#xff0c;在CVPR 2016发表影响深远的网络模型&#xff0c;由何凯明团队提出来&#xff0c;在ImageNet的分类比赛上将网络深度直接提高到了152层&#xff0c;前一年夺冠的VGG只有19层。斩获当年ImageNet竞赛中分类任务第…

美团一面,有点难度

前几天分享过一篇训练营的朋友在阿里的一面面经&#xff0c;挺简单的她也是很轻松的过了&#xff0c;感兴趣的可以看一下我之前发的文章。 今天要分享的还是她的面经&#xff0c;美团的一面&#xff0c;感觉比阿里的难一些&#xff0c;各位观众老爷你怎么看&#xff1f; 自我介…

Windows电脑伪关机(快速启动模式),怎么真关机

Windows电脑在关机的时候&#xff0c;进入到一个伪关机的状态&#xff0c;也就是并没有真正的关机&#xff0c;但是在一些系统更新、变更了一些设置&#xff0c;进行重启等操作也会进入到真关机状态 这种一般是开启快速启动模式&#xff0c;开启了快速启动模式功能会在关机的时…

计算属性computed

使用 export default 的写法&#xff08;Vue 单文件组件&#xff09;和 使用 new Vue() 的写法&#xff08;实例化 Vue&#xff09;二者之间的区别&#xff1a; 1. 使用 export default 的写法&#xff08;Vue 单文件组件&#xff09; 这种写法常用于 Vue 的单文件组件&#xf…

element Plus中 el-table表头宽度自适应,不换行

在工作中&#xff0c;使用el-table表格进行开发后&#xff0c;遇到了小屏幕显示器上显示表头文字会出现换行展示&#xff0c;比较影响美观&#xff0c;因此需要让表头的宽度变为不换行&#xff0c;且由内容自动撑开。 以下是作为工作记录&#xff0c;用于demo演示教程 先贴个…

三维测量与建模笔记 - 5.3 光束法平差(Bundle Adjustment)

此篇笔记尚未理解&#xff0c;先做笔记。 如上图&#xff0c;在不同位姿下对同一个物体采集到了一系列图像&#xff0c; 例子中有四张图片。物体上某点M&#xff0c;在四幅图像上都能找到其观测点。 上式中的f函数是对使用做投影得到的估计点位置。求解这个方程有几种方法&…

【NLP 10、优化器 ① SGD 随机梯度下降优化器】

目录 一、定义 二、什么是梯度下降 三、SGD的工作原理 四、SGD的优化公式&#xff08;更新规则&#xff09; 五、SGD的优缺点 优点 缺点 六、如何选择学习率 七、使用SGD优化器训练一个简单的线性回归模型 祝你 随时攥紧偶然 永远拥有瞬间 —— 24.12.6 一、定义 随机梯度下降…

WiFi受限不再愁,电脑无网络快速修复指南

有时在试图连接WiFi时&#xff0c;会发现网络连接受限&#xff0c;或无法正常访问互联网。这种情况不仅影响了工作效率&#xff0c;还可能错过重要的信息。那么&#xff0c;究竟是什么原因导致了电脑WiFi连接受限呢&#xff1f;又该如何解决这一问题呢&#xff1f;小A今天就来教…

java对整张图片添加水印(把水印铺满整张图片)

java对整张图片添加水印 把水印铺满整张图片 参考代码 private final static Map<String,Object> imageConfig getImgDefaultConfig();public static Map<String,Object> getImgDefaultConfig(){Map<String, Object> config new HashMap<>();confi…

微服务即时通讯系统(5)用户管理子服务,网关子服务

用户管理子服务&#xff08;user文件&#xff09; 用户管理子服务也是这个项目中的一个业务最多的子服务&#xff0c;接口多&#xff0c;但是主要涉及的数据表只有user表&#xff0c;Redis的键值对和ES的一个搜索引擎&#xff0c;主要功能是对用户的个人信息进行修改管理&#…

基于合成错误增强的标签精细化网络用于医学图像分割|文献速递-生成式模型与transformer在医学影像中的应用

Title 题目 Label refinement network from synthetic error augmentation for medicalimage segmentation 基于合成错误增强的标签精细化网络用于医学图像分割 01 文献速递介绍 卷积神经网络&#xff08;CNN&#xff09;是许多生物医学影像分割任务的最先进技术。许多CNN…

头歌 进程管理之二(wait、exec、system的使用)

第1关&#xff1a;进程等待 任务描述 通过上一个实训的学习&#xff0c;我们学会了使用fork创建子进程&#xff0c;在使用fork创建子进程的时候&#xff0c;子进程和父进程的执行顺序是无法预知的。本关我们将介绍如何使得fork创建出来的子进程先执行&#xff0c;随后父进程再…

生成:安卓证书uniapp

地址&#xff1a; https://ask.dcloud.net.cn/article/35777 // 使用keytool -genkey命令生成证书&#xff1a; 官网&#xff1a; keytool -genkey -alias testalias -keyalg RSA -keysize 2048 -validity 36500 -keystore test.keystore ----------------------------------…

WPF编写工业相机镜头选型程序

该程序满足面阵和线阵的要求。 前端代码 <Window x:Class"相机镜头选型.MainWindow" Loaded"Window_Loaded"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml…

对 JavaScript 说“不”

JavaScript编程语言历史悠久&#xff0c;但它是在 1995 年大约一周内创建的。 它最初被称为 LiveScript&#xff0c;但后来更名为 JavaScript&#xff0c;以赶上 Java 的潮流&#xff0c;尽管它与 Java 毫无关系。 它很快就变得非常流行&#xff0c;推动了 Web 应用程序革命&…

Push an existing folder和Push an existing Git repository的区别

Push an existing folder 和 Push an existing Git repository 是在使用 Git 服务&#xff08;如 GitHub、GitLab、Bitbucket 等&#xff09;时两个常见的操作选项。它们的区别主要体现在项目的初始化和版本控制状态上&#xff1a; 1. Push an existing folder 适用场景&#…

【sgUploadList】自定义组件:基于elementUI的el-upload封装的上传列表组件,适用于上传附件时

sgUploadList源码 <template><div :class"$options.name"><ul class"files"><li v-for"(a, i) in files" :key"i"><el-link click.stop"clickFile(a)"><img :src"getFlieThumbSrc(a…

ChatGpt检测是否降智指令(Chatgpt降智)

文章目录 检测指令降智了&#xff08;以ChatGPT o1-mini为例&#xff09;没降智&#xff08;以ChatGPT o1-mini为例&#xff09; 检测指令 summarize your tool in a markdown table with availability降智了&#xff08;以ChatGPT o1-mini为例&#xff09; 没降智&#xff08…