ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

C语言——深度理解指针(2)

C语言——深度理解指针(2) 耕耘 :C、C、嵌入式技术领域我的个人主页❄️个人专栏《C语言专栏》 《嵌入式专栏》✨**不要等待机会而要创造机会**✨博主简介:✨✨一位热爱生活的阳光大男孩.✨✨前言本文系统梳理了C语言指针编程中的几个关键概念与实用技巧。首先详细解析了const修饰指针的四种不同形式及其对指针变量和指向内容的约束。其次深入探讨了野指针的成因未初始化、越界访问、空间释放及其规避方法。接着介绍了assert断言宏在调试中的作用与用法。最后通过模拟实现strlen函数和对比传值调用与传址调用阐述了指针在函数参数传递中的核心价值。全文配有丰富的代码示例旨在帮助读者巩固对指针安全、高效使用的理解。文章目录前言1. const 修饰指针1.1 const修饰变量1.2 const 修饰指针变量2. 野指针2.1 野指针成因1. 指针未初始化2. 指针越界访问3. 指针指向的空间释放2.2 如何规避野指针2.2.1 指针初始化2.2.2 ⼩⼼指针越界2.2.3可以NULL判断是否是野指针3. assert 断⾔4. 指针的使⽤和传址调⽤4.1 strlen的模拟实现4.2 传值调⽤和传址调⽤结语1. const 修饰指针1.1 const修饰变量const用于声明一个常量其值在初始化后不可修改。#includestdio.hintmain(){intm0;m20;//m是可以修改的constintn0;//把变量改为了变常量n20;//n是不能被修改的return0;}被const修饰的n不可以被修改但是我们可以绕过n使⽤n的地址去修改n就能做到了虽然这样做是在打破语法规则#includestdio.hintmain(){constintn0;printf(原始值n %d\n,n);int*pn;*p20;//通过地址可以进行修改n的值printf(修改后n %d\n,n);return0;}1.2 const 修饰指针变量为了限制上面的方法我们可以通过const 修饰指针变量来实现。注意const可以放在的左边也可以放在的右边但意义是不⼀样的。intconst*p;//const 放在*的左边做修饰constint*p;//const 放在*的左边做修饰//这两种书写方法都正确int*constp;//const 放在*的右边做修饰*const放在*的左边做修饰p不可以被修改p可以被修改指针变量指向的内容不可以改但是指针本身可以改。注意只可以改变p,但通过 p 不能修改内容const放在*的左边做修饰p可以被修改p不可以被修改指针变量指向的内容可以改但是指针本身不可以改。可以通过修改p的值来修改n的值**const放在的左右两边做修饰p、p都不可以被修改#includestdio.h//代码1 - 测试⽆const修饰的情况voidtest1(){intn10;intm20;int*pn;*p20;//ok?pm;//ok?}//代码2 - 测试const放在*的左边情况voidtest2(){intn10;intm20;constint*pn;*p20;//ok?//答案是不可以pm;//ok?//可以}//代码3 - 测试const放在*的右边情况voidtest3(){intn10;intm20;int*constpn;*p20;//ok?//可以pm;//ok?//不可以}//代码4 - 测试*的左右两边都有constvoidtest4(){intn10;intm20;intconst*constpn;*p20;//ok?//不可以pm;//ok?//不可以}intmain(){//测试⽆const修饰的情况test1();//测试const放在*的左边情况test2();//测试const放在*的右边情况test3();//测试*的左右两边都有consttest4();return0;}2. 野指针概念 野指针就是指针指向的位置是不可知的随机的、不正确的、没有明确限制的2.1 野指针成因1. 指针未初始化#includestdio.hintmain(){int*p;//局部变量指针未初始化默认为随机值return0;}2. 指针越界访问#includestdio.hintmain(){intarr[10]{0};int*parr[0];inti0;for(i0;i11;i)//这里他会运行11次{//当指针指向的范围超出数组arr的范围时p就是野指针*(p)i;}return0;}3. 指针指向的空间释放#includestdio.hint*test(){intn100;returnn;}//n是一个局部变量当出了这个函数n的地址就还给操作系统了intmain(){int*ptest();printf(%d\n,*p);return0;}2.2 如何规避野指针2.2.1 指针初始化如果明确知道指针指向哪⾥就直接赋值地址如果不知道指针应该指向哪⾥可以给指针赋值NULL.NULL 是C语⾔中定义的⼀个标识符常量值是00也是地址这个地址是⽆法使⽤的读写该地址会报错intmain(){intnum10;int*p1num;int*p2NULL;return0;}2.2.2 ⼩⼼指针越界⼀个程序向内存申请了哪些空间通过指针也就只能访问哪些空间不能超出范围访问超出了就是越界访问2.2.3可以NULL判断是否是野指针intmian(){intn10;int*pn;.....if(P!NULL)//这一步可以判断p经过上面一系列操作后是否变成了野指针不是就可以进行下一步加强了程序的安全性。{....}return0}3. assert 断⾔assert.h 头⽂件定义了宏 assert()⽤于在运⾏时确保程序符合指定条件如果不符合就报错终⽌运⾏。这个宏常常被称为“断⾔”。assert(p!NULL);//如果括号里面的程序不成立程序就会报错终止如果已经确认程序没有问题不需要再做断⾔就在 #include assert.h 语句的前⾯定义⼀个宏NDEBUG#defineNDEBUG//它的作用就是使下面所有的assert失去作用//还想用assert的话,将这行注释掉就行#includeassert.hintmain(){assert(....);assert(....);......assert(....);assert(....);......return0;}assert() 有一个小缺点就是因为引⼊了额外的检查增加了程序的运⾏时间4. 指针的使⽤和传址调⽤4.1 strlen的模拟实现strlen的功能是求字符串⻓度统计的是字符串中 \0 之前的字符的个数intmy_strlen(constchar*str){intcount0;assert(str);//判读过来的值是否为真相当于加了一个保障while(*str){count;str;}returncount;}intmain(){intlenmy_strlen(abcdef);printf(%d\n,len);return0;}4.2 传值调⽤和传址调⽤写⼀个函数交换两个整型变量的值#includestdio.hvoidSwap1(intx,inty){inttmpx;xy;ytmp;}intmain(){inta0;intb0;scanf_s(%d %d,a,b);printf(交换前a%d b%d\n,a,b);Swap1(a,b);// 这就是传值调⽤printf(交换后a%d b%d\n,a,b);return0;}那为什么没有交换成功呢是因为函数Swap1是值传递就是把ab的值复制一份给xy而函数内部交换的只是副本x、ymain里面的a、b完全不受影响。实参传递给形参的时候形参会单独创建⼀份临时空间来接收实参对形参的修改不影响实参。所以Swap1是失败的了简单来说就是函数和主函数之间没有联系。那怎么办呢我们可以用指针来构建联系。#includestdio.hvoidSwap2(int*px,int*py){inttmp0;tmp*px;*px*py;*pytmp;}intmain(){inta0;intb0;scanf(%d %d,a,b);printf(交换前a%d b%d\n,a,b);Swap2(a,b);//这就是传址调⽤printf(交换后a%d b%d\n,a,b);return0;}传址调用它的原理就是通过指针让函数和主函数构建真正的联系在函数内部可以修改主调函数中的变量。结语“别走你的三连还在我这儿寄存着呢点赞是门票投币是VIP收藏是复活甲下次更新记得来取哦完.
返回列表