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,一经查实,立即删除!

相关文章

dart类和对象

基本定义 类 类是一个包裹多个对象成员的一个整体&#xff0c;可以通过实例化获取类的对象&#xff0c;通过对象操纵内部对象 对象 对象具有由函数和数据&#xff08;分别为方法和 实例变量&#xff09;组成的成员。调用方法时&#xff0c;您会在对象上调用它&#xff1a;该…

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

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

【Vulkan入门】04-开启Debug输出

目录 先叨叨git信息关键代码和主要APIVulkanEnv::SetDebugUtilMessenger()VulkanEnv::CreateVkInstance() 题外话 先叨叨 到上篇为止我们已经作了很多事情了。建立了Instance、挑选了物理设备、建立的Device和Queue。 之前做的都是相对简单和线性的工作&#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; 自我介…

VS下网络快速连接检测实现

一.问题&#xff1a; VS实现PC软件和单片机的网络连接的时候&#xff0c;如果网线没有插入&#xff0c;检测连接失败&#xff0c;一般设置网络连接为非阻塞方式&#xff0c;但是如果单片机返回比较慢&#xff0c;会导致正常情况下也连不上&#xff0c;下面代码通过 设置等待方…

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…

【开发文档】资源汇总,持续更新中......

文章目录 AI大模型数据集PytorchPythonUltralyticsOpenCVNetronSklearnCMakeListsNVIDIADocker刷题网站持续更新&#xff0c;欢迎补充 本文汇总了一些常用的开发文档资源&#xff0c;涵盖了常用AI大模型、刷题网站、Python、Pytorch、OpenCV、TensorRT、Docker 等技术栈。通过这…

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

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

模型预测控制(Model Predictive Control)

学习模型预测控制的几个阶段&#xff1a; 1.迷茫&#xff0c;一头雾水&#xff0c;立刻忘记: 一个易懂的MPC理论推导过程教程 2.感兴趣&#xff0c;知道了大概讲些什么东西&#xff0c;但是不知道如何应用: 一个系统讲解MPC的入门教程1 3.开始深度学习&#xff1a; 待续…

2021高等代数【南昌大学】

证明多项式 f ( x ) = 1 + x + x 2 2 ! + ⋯ + x n n ! f(x) = 1 + x + \frac{x^2}{2!} + \cdots + \frac{x^n}{n!} f(x)=1+x+2!x2​+⋯+n!xn​ 无重根。f ( x ) − f ′ ( x ) = x n n ! f(x) - f(x) = \frac{x^n}{n!} f(x)−f′(x)=n!xn​ ( f ( x ) , f ′ ( x ) ) = ( f (…

鸿蒙Next通过oss上传照片到阿里云

前言 最近在写纯血鸿蒙的APP&#xff0c;需要用到oss上传照片&#xff0c;之前的客户端 Android 和 IOS 都已经实现了&#xff0c;获取的阿里云签名的上传地址是服务端实现的&#xff0c;相信大部分公司都是这样的模式&#xff0c;服务端也是调用阿里云的SDK来实现的&#xff…

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

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

C++学习0.2: RAII

引用&#xff1a; 【代码质量】RAII在C编程中的必要性_raii 在c中的重要性-CSDN博客 C RAII典型应用之lock_guard和unique_lock模板_raii lock-CSDN博客 前言: 常用的线程间同步/通信&#xff08;IPC&#xff09;方式有锁&#xff08;互斥锁、读写锁、自旋锁&#xff09;、…

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

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

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

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

使用CancellationTokenSource来控制长时间sql查询中断

前端 <!-- 透明的覆盖层&#xff0c;显示在页面上方&#xff0c;包含进度条 --><Grid Visibility"{Binding IsLoading}" Background"Transparent" HorizontalAlignment"Stretch" VerticalAlignment"Stretch" ZIndex"1&…

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

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