C语言入门,深入理解指针(2)

发布时间:2026/10/5 18:17:56
C语言入门,深入理解指针(2)
目录1. const修饰指针变量1. const修饰变量2. const修饰指针变量2. 野指针1. 野指针成因2. 如何规避野指针3. assert断言4. 指针的使用和传址调用1. strlen的模拟实现2. 传值调用和传址调用前言本文主要讲解C语言中指针相关的进阶知识包括const修饰指针变量、野指针的成因与规避、assert断言的用法以及传值调用与传址调用的区别。通过本文的学习你将能更安全、规范地使用指针。1.const修饰指针变量1.const修饰变量首先我先去创建一个变量a并修改它最后输出a现在我不想让这个a被修改那么就要使用一种方式去限制它的修改这时候我学习到了const可以去发挥这种作用先介绍const的基本概念const表示常属性的意思就是不能被改变的意思限制变量的修改知道它的作用之后我就要去使用它了改进代码如下此时系统爆出了警告表示我已经限制了a的修改在这里a本质上还是变量只是在语法层面上限制了a的修改这里a被称为常变量是最合适的。拓展在之后要学的C里const修饰的就不是变量直接就是常量了。但是现在又换了另一种思路说我不直接修改你的a我通过指针变量得到a的地址再通过地址去找到那块空间去修改那块空间的存放的数据这个方法好像有点道理我尝试去改进代码运行一下发现修改成功了这就有点像这么一个例子如下图我有一间房子我每次回家都要从门进去每次出门都要锁门但是有一天我忘带钥匙了此时门打不开那我为了进入家里我被迫翻越窗户进到家中才达到我回家的目的这个场景是不是有点像上面的情况了。但是我最初的目的是想要a不被修改而上面是拿到地址才能去修改a的如何让p拿到a的地址也不能成功去修改a呢这就引出了我接下来要讲的知识继续往下看2.const修饰指针变量我的解决办法就是用const修饰指针变量限制指针变量p指向的对象a被修改但是在实现这个代码之前还是得先来讲一下这个const修饰指针变量的基本用法及意义吧。const修饰指针变量一般可以放在星号的左边也可以放在星号的右边如下那么const的位置不同表达的含义又有什么不同呢首先说明一下pa和pa的区别如下图**const在星号的左边限制的是p也就是p指向的对象不能被修改但是指针变量p的指向可以被修改**const在星号的右边限制的是p也就是指针变量p的指向不能被修改但是p指向的对象可以被修改这时候我又要举个例子帮助大家理解记忆了吃凉皮的例子首先创建2个变量名为男孩a和男孩b分别有10块和100块再创建一个指针变量p名为女孩p,取地址a赋给p,说明男孩a和女孩p是男女朋友当有一天女孩想吃凉皮了她想要男孩请他吃凉皮但是男孩不想要花钱所以在星号的左边加上const限制他兜里的10块被修改此时女孩说你不请我吃凉皮但是我有权利换男朋友啊男孩a明显慌了他告诉女孩“我可以请你吃凉皮但是你不能换男朋友此时在星号的右边加上了const限制女孩不能换男朋友他兜里的10块可以被修改这个故事很好的说明了const在星号的不同位置上所起到的作用提醒一嘴如果既不愿意给你花钱也不愿意让你换男朋友你大抵是遇上渣男了。2.野指针学习完const,接下来我们要去讲一类指针叫做野指针。先来了解一下它的概念吧概念野指针就是指针指向的位置是不可知的随机的、不正确的、没有明确限制的说白了就是指针指向的空间不属于当前的程序。1.野指针成因1.指针未初始化原因指针变量pa未初始化时pa属于局部变量局部变量不初始化它里边存放的是1个随机值指针变量pa没有明确指向的是内存的哪一块空间。2.指针的越界访问原因p存放首元素的地址我们故意让它循环11次p在前10次循环时p都是一个合法的指针但当加到10时p10所指向的空间已经不属于当前程序向内存申请的空间了此时指针变成了野指针。3.指针指向的空间释放原因虽然说test函数返回让p接受到了n的地址但是函数的调用结束了局部变量n已经被销毁了。n已经把那块空间还回去了然后你想要通过接受到的n的地址去找到np所指向的那块内存空间已经不属于当前程序了即使你找到了那块地址也只能获得1个随机值。2.如何规避野指针1.指针初始化情况一明确知道指针变量要初始化为什么值。inta10;int*paa;情况二不知道指针变量应该给什么值可以给指针赋值为NULLint*pNULL;关于NULL的解释NULL表示空指针的意思空指针就是没有指向任何有效的空间暂时不要使用它。NULL 作为C语言中定义的一个标识符常量值是00也是地址这个地址是无法使用的读写该地址会报错。举个形象的例子大家就明白了将野指针看作是一条野狗野狗没拴绳很危险此时NULL相当于一条狗绳将野狗暂时拴起来也就是指针变量防止变成野指针。2.小心指针越界当前程序向内存申请了哪些空间只能允许指针访问哪些空间不能超出范围访问不属于你的空间不访问只访问自己的空间。3.指针变量不再使用时及时置为NULL指针使用之前检查有效性如下图4.避免返回局部变量的地址返回栈空间的地址不返回局部变量的地址就可以了与返回局部变量的区别函数返回局部变量时将局部变量的数据暂时存放在寄存器内函数调用结束局部变量被销毁主函数内的变量通过关联寄存器得到局部变量的数据。3.assert断言我们前面不是通过if条件去判断指针变量是否为空指针吗而在这里其实还有1种更好的方法去判断就是assert断言。assert.h头文件定义了宏assert(),用于在运行时确保程序符合指定条件如果不符合就报错终止运行。这个宏常常被称为**“断言”**。assert()宏在括号里可以接受一个你想判断的表达式作为参数若表达式为真即返回值为非0assert不会产生任何作用程序继续运行若表达式为假即返回值为0assert发生报错控制台会显示一条错误的信息包含没有通过的表达式以及包含这个表达式的文件名和行号如下图所示当然使用assert()也要包含对应的头文件#include assert.h如果使用assert()后确认程序没有问题我们可以把它给关掉如果不关掉程序始终会默认执行assert()这条语句增加程序运行的时间我们在头文件前面定义一个宏NDEBUG,就可以关掉断言如果下次还想使用直接将#define NDEBUG注释或移除还有一点要注意的就是在VS这样的集成开发环境下我们可以在Debug中使用在Release版本直接就是把assert()优化掉了就是不起作用了。4.指针的使用和传址调用1.strlen的模拟实现在上节课我们已经对strlen函数通过一个函数实现了功能的模拟今天我们在重新地去梳理一下当我们学过const和assert断言后我们可以把代码去变得更加安全一些2.传值调用和传址调用学习指针的目的是为了用指针解决问题那么有什么问题非指针不可呢例如写一个函数交换2个整型变量的值你的第一反应可能会想创建一个临时变量去帮助这2个整型变量的交换我们根据这个思路写出下面的代码运行结果我们发现a和b的值未发生交换这是为什么呢我们不妨调试一下调试过程中我们可以看到当函数将实参a、b传递给形参时形参x、y单独向内存申请了空间这也导致了它们内存空间不一样在交换过程中形参的改变对实参没有影响交换x、y不影响a、b的值自然就达不到我们想要的结果了。这种方式叫做**“传值调用”**。结论实参传递给形参的时候形参会单独创建一份临时空间来接收实参对形参的修改不影响实参。所以Swap1是失败的了。那么怎么样将实参和形参的改变联系起来呢我们想让函数内部操作的参数就是主函数中的参数那这时候我们就要用到指针了通过main函数对swap函数的调用将a、b的地址传递给swap函数swap函数在间接使用该地址从而实现对a、b的交换。改进代码如下这种方式叫做**“传址调用”**可以让函数和主调函数之间建立真正的联系在函数内部可以修改主调函数中的变量。所以未来函数中只是需要主调函数中的变量值来实现计算就可以采用传值调用。如果函数内部要修改主调函数中的变量的值就需要传址调用。总结本文围绕C语言指针的进阶使用展开首先介绍了const修饰指针变量的两种形式及其区别接着分析了野指针的成因并给出了初始化、防越界、置NULL等规避方法然后介绍了assert断言这一更安全的指针检查方式最后通过交换变量的例子清晰区分了传值调用与传址调用的适用场景。掌握这些内容能帮助你在实际编程中写出更健壮、更安全的指针代码。