news 2026/1/17 15:53:07

C 语言指针进阶教程:const 修饰、野指针规避与传址调用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C 语言指针进阶教程:const 修饰、野指针规避与传址调用

🏠个人主页:黎雁
🎬作者简介:C/C++/JAVA后端开发学习者
❄️个人专栏:C语言、数据结构(C语言)、EasyX、游戏、规划
✨ 从来绝巘须孤往,万里同尘即玉京

文章目录

    • 前景回顾:上一篇指针核心知识点速记 📝
    • 一、const 修饰指针:限制指针的修改权限 🔒
      • 1. const 修饰变量:常变量的特性
        • 补充方法:使用指针间接修改常变量
      • 2. const 修饰指针变量:不同位置的不同效果
        • 代码验证示例
    • 二、野指针:C语言中的危险隐患 💣
      • 1. 野指针的定义
      • 2. 野指针的三大成因 🚫
        • ① 指针未初始化
        • ② 指针越界访问
        • ③ 指针指向的空间被释放
      • 3. 规避野指针的四大方法 ✅
        • ① 指针必须初始化
        • ② 避免指针越界访问
        • ③ 指针不用时及时置`NULL`,使用前检查有效性
        • ④ 避免返回局部变量的地址
    • 三、assert 断言:程序运行时的检查工具 🛂
      • 1. assert 的基本概念
      • 2. assert 的使用示例
      • 3. assert 的优势
        • ① 精准定位问题
        • ② 可以灵活开启和关闭
    • 四、指针的使用和传址调用:指针的实际应用 💪
      • 1. 模拟实现 strlen 函数
      • 2. 值调用与传址调用:交换两个整数的对比
        • ① 值调用:无法实现交换功能
        • ② 传址调用:成功实现交换功能
    • 写在最后 📝

继上一篇的指针基础内容后,本篇将继续深入讲解指针的进阶知识,主要包含const修饰指针、野指针、assert断言以及指针的使用和传址调用四个核心模块,内容详实且条理清晰,帮助大家彻底掌握这些关键知识点。

前景回顾:上一篇指针核心知识点速记 📝

上一篇文章链接:【C语言指针精讲】从内存到运算,吃透指针核心逻辑

要学好本篇的进阶内容,需要先巩固上一篇的关键知识点,打好扎实的基础:

  1. 内存与地址:内存以1字节为基本存储单元,每个单元都有唯一的编号,这个编号就是地址,也被称为指针
  2. 指针变量:是专门用于存储地址的变量,定义格式为类型* 变量名,使用&可以获取变量的地址,使用*可以解引用指针,从而操作目标变量。
  3. 指针大小:仅与系统位数相关,32位系统下指针变量占4字节,64位系统下占8字节,和指针指向的变量类型没有关系。
  4. 指针类型的意义:决定了解引用时操作的字节数,以及指针进行±整数运算时的步长,例如int*类型指针步长为4,char*类型指针步长为1。
  5. 指针运算:支持±整数、指针减指针(要求两个指针指向同一块连续内存)以及关系运算,是高效遍历数组的重要方式。

一、const 修饰指针:限制指针的修改权限 🔒

在C语言中,const的作用是限制变量或指针的修改权限,让程序的逻辑更加严谨。我们可以分为两种情况来理解它的用法。

1. const 修饰变量:常变量的特性

const修饰的变量会成为常变量,它的本质仍然是变量,但无法直接对其进行修改操作。

#include<stdio.h>intmain(){constintn=10;// n为常变量,不能直接修改// n=100; // 报错,直接修改常变量的操作不被允许printf("%d\n",n);// 输出:10return0;}
补充方法:使用指针间接修改常变量

虽然不能直接修改常变量的值,但可以借助指针,间接修改常变量对应的内存数据。

#include<stdio.h>intmain(){constintn=10;int*p=&n;// 让指针指向常变量n的地址*p=100;// 通过解引用指针,间接修改n的值printf("%d\n",n);// 输出:100return0;}

2. const 修饰指针变量:不同位置的不同效果

const修饰指针变量时,放置的位置不同,产生的限制效果也截然不同,核心判断依据是const*的左侧还是右侧。

const的位置格式示例限制规则
*左边const int *p/int const *p限制的是指针指向的内容*p的值不能修改,但指针变量p本身可以指向其他地址
*右边int *const p限制的是指针变量本身p不能指向其他地址,但指针指向的内容*p可以修改
*两边const int *const p对指针和指针指向的内容都有限制,p不能更换指向,*p的值也不能修改
代码验证示例
#include<stdio.h>intmain(){constintn=10;intb=20;// 情况1:const在*左边constint*p1=&n;// *p1 = 100; // 报错,无法修改指针指向的内容p1=&b;// 合法,指针可以指向其他地址// 情况2:const在*右边int*constp2=&n;*p2=100;// 合法,可以修改指针指向的内容// p2 = &b; // 报错,指针不能指向其他地址printf("%d\n",n);return0;}

二、野指针:C语言中的危险隐患 💣

野指针是C语言编程中需要重点防范的问题,一旦出现野指针,很容易导致程序崩溃或出现不可预期的错误。

1. 野指针的定义

野指针指的是指向位置不可知的指针,具体来说,就是指针指向的内存空间不属于当前程序,对这类指针进行访问或操作,属于非法的内存操作。

2. 野指针的三大成因 🚫

① 指针未初始化

局部指针变量如果在定义时没有进行初始化,编译器会为其分配一个随机的内存地址,这个指针就会成为野指针。

#include<stdio.h>intmain(){int*p;// 局部指针变量未初始化,指向随机地址*p=20;// 操作野指针,会导致程序异常return0;}
② 指针越界访问

当指针的操作范围超出了目标内存的合法边界时,指针就会变成野指针,最常见的场景是数组遍历的越界。

#include<stdio.h>intmain(){intarr[10]={0};int*p=&arr[0];for(inti=0;i<=11;i++)// 循环次数超出数组元素个数,导致越界{*(p++)=i;// 指针越界后,转变为野指针}return0;}
③ 指针指向的空间被释放

函数内部的局部变量,其生命周期仅限于函数运行期间,函数执行结束后,局部变量的内存空间会被系统回收。如果函数返回局部变量的地址,得到的指针就是野指针。

#include<stdio.h>int*test(){intn=100;// n是局部变量,函数结束后内存会被释放return&n;// 返回局部变量的地址,得到野指针}intmain(){int*p=test();printf("%d\n",*p);// 非法访问已释放的内存,结果不可预期return0;}

3. 规避野指针的四大方法 ✅

① 指针必须初始化
  • 如果明确知道指针的指向,直接将目标变量的地址赋值给指针;
  • 如果暂时不确定指针的指向,就将NULL赋值给指针。NULL是C语言定义的常量,本质是地址0,这个地址无法进行读写操作。
inta=10;int*p1=&a;// 明确指向,初始化方式安全int*p2=NULL;// 暂时无指向,赋值为NULL
② 避免指针越界访问

指针只能访问当前程序已经申请过的内存空间,在编写代码时,要严格控制指针的操作范围,杜绝越界情况的发生。

③ 指针不用时及时置NULL,使用前检查有效性

当指针使用完毕后,将其赋值为NULL,后续再次使用这个指针前,先判断它是否为NULL,确认非空后再进行操作。

#include<stdio.h>intmain(){intarr[10]={1,2,3,4,5,6,7,8,9,10};int*p=&arr[0];for(inti=0;i<10;i++){*(p++)=i;}p=NULL;// 指针使用完毕,置为NULL// 重新使用前进行检查p=&arr[2];if(p!=NULL)// 确认指针非空{// 执行安全的操作}return0;}
④ 避免返回局部变量的地址

局部变量的作用域仅限于函数内部,函数执行结束后其内存会被回收,因此不要将局部变量的地址作为函数的返回值。

三、assert 断言:程序运行时的检查工具 🛂

assert断言是C语言中用于程序调试的重要工具,能够帮助开发者在程序运行过程中,及时发现隐藏的问题。

1. assert 的基本概念

assert是定义在<assert.h>头文件中的宏,它接收一个表达式作为参数,运行时的判断规则如下:

  • 如果表达式的结果为真(非0),程序正常执行,assert不会产生任何影响;
  • 如果表达式的结果为假(0),程序会立刻终止运行,并在标准错误流中输出错误信息,包括未通过的表达式、所在的文件名和行号。

2. assert 的使用示例

#include<stdio.h>#include<assert.h>intmain(){int*p=NULL;assert(p!=NULL);// 检查指针是否为空printf("程序正常运行\n");// 表达式为假,此句不会执行return0;}

程序运行后会输出报错信息,示例如下:
Assertion failed: p != NULL, file xxx.c, line 5

assert的检查范围不局限于指针,任何表达式都可以作为它的参数:

#include<stdio.h>#include<assert.h>intmain(){inta=10;assert(a==5);// 检查变量a的值是否为5printf("程序正常运行\n");return0;}

3. assert 的优势

① 精准定位问题

当程序出现问题时,assert能够直接指出错误所在的文件和行号,无需开发者手动添加调试代码,大大提升调试效率。

② 可以灵活开启和关闭

如果确认程序没有问题,不需要再进行断言检查,可以在引入<assert.h>头文件之前,定义宏NDEBUG,这样编译器就会禁用当前文件中所有的assert语句。

#defineNDEBUG// 定义该宏,禁用assert#include<assert.h>

一般情况下,Debug版本的程序默认开启assert,Release版本的程序默认禁用assert

四、指针的使用和传址调用:指针的实际应用 💪

学习指针的最终目的是将其运用到实际编程中,接下来通过两个经典案例,讲解指针的实用技巧。

1. 模拟实现 strlen 函数

strlen是C语言的库函数,定义在<string.h>头文件中,功能是统计字符串中\0之前的字符个数。函数原型为size_t strlen(const char *str);

在模拟实现这个函数时,constassert都能发挥重要作用:

#include<stdio.h>#include<assert.h>size_tmy_strlen(constchar*p)// const修饰,保证不会修改字符串内容{assert(p!=NULL);// 断言检查,防止空指针解引用size_tcount=0;while(*p!='\0')// 遍历字符串,直到遇到结束符{count++;p++;// 指针移动,指向下一个字符}returncount;}intmain(){chararr[]="abcdef";size_tlen=my_strlen(arr);printf("%zd\n",len);// 输出:6return0;}

关键点解析:const char *p的写法可以避免函数内部意外修改字符串内容;assert(p != NULL)能够防止传入空指针导致程序崩溃。

2. 值调用与传址调用:交换两个整数的对比

我们以编写交换两个整数的函数为例,对比值调用和传址调用的区别。

① 值调用:无法实现交换功能

值调用时,函数的形参是实参的临时拷贝,形参拥有独立的内存空间,对形参的修改不会影响到实参。

#include<stdio.h>voidSwap(intx,inty)// x、y是实参的临时拷贝{intz=x;x=y;y=z;}intmain(){inta=10,b=20;printf("交换前:a=%d b=%d\n",a,b);// 输出:交换前:a=10 b=20Swap(a,b);// 值调用,传递的是变量的值printf("交换后:a=%d b=%d\n",a,b);// 输出:交换后:a=10 b=20return0;}
② 传址调用:成功实现交换功能

传址调用时,函数接收的是实参的地址,通过指针解引用,可以直接操作实参对应的内存空间,从而实现修改实参的目的。

#include<stdio.h>voidSwap2(int*pa,int*pb)// 接收实参的地址{intc=*pa;// c获取a的值*pa=*pb;// 将b的值赋给a*pb=c;// 将原来a的值赋给b}intmain(){inta=10,b=20;printf("交换前:a=%d b=%d\n",a,b);// 输出:交换前:a=10 b=20Swap2(&a,&b);// 传址调用,传递变量的地址printf("交换后:a=%d b=%d\n",a,b);// 输出:交换后:a=20 b=10return0;}

核心逻辑:传址调用突破了函数的局部限制,让函数内部能够直接对主函数中的变量进行修改。

写在最后 📝

本篇的指针进阶知识点,核心围绕安全实用两个关键词展开:

  1. const的合理使用,能够限制指针和变量的修改权限,让程序逻辑更加严谨;
  2. 认清野指针的成因,并掌握对应的规避方法,是保证程序稳定运行的关键;
  3. assert断言是高效的调试工具,能够帮助开发者快速定位程序问题;
  4. 传址调用是指针的核心应用场景之一,能够实现函数对外部变量的修改操作。

指针的学习需要循序渐进,建议大家多编写代码、多调试运行,观察指针的指向变化,加深对知识点的理解。下一篇我们将继续讲解指针与,敬请关注。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/17 9:57:09

Linly-Talker本地部署避坑指南(附性能调优建议)

Linly-Talker本地部署避坑指南&#xff08;附性能调优建议&#xff09; 在数字人技术加速落地的今天&#xff0c;越来越多企业与开发者希望构建具备自然交互能力的虚拟形象。然而&#xff0c;市面上大多数方案要么依赖云端API、存在数据泄露风险&#xff0c;要么系统复杂、难以…

作者头像 李华
网站建设 2026/1/9 16:35:42

Linly-Talker支持模型版本回滚,保障系统稳定性

Linly-Talker支持模型版本回滚&#xff0c;保障系统稳定性 在虚拟主播24小时不间断直播、智能客服全天候响应用户的今天&#xff0c;数字人早已不再是实验室里的概念玩具&#xff0c;而是真正走进了商业场景的核心。然而&#xff0c;当一套集成了语言理解、语音识别、语音合成和…

作者头像 李华
网站建设 2025/12/29 14:06:46

数字人求职简历:应聘者用AI展示自我介绍的新方式

数字人求职简历&#xff1a;应聘者用AI展示自我介绍的新方式 在一场线上招聘会上&#xff0c;一位应聘者没有提交传统的PDF简历&#xff0c;而是附上了一段90秒的视频——画面中&#xff0c;一个与他本人高度相似的虚拟形象正从容不迫地进行自我介绍&#xff0c;语调自然、口型…

作者头像 李华
网站建设 2026/1/12 6:33:51

Linly-Talker推理延迟优化技巧(基于TensorRT加速)

Linly-Talker推理延迟优化技巧&#xff08;基于TensorRT加速&#xff09; 在虚拟主播、智能客服和数字员工等实时交互场景中&#xff0c;用户对“响应快、反应自然”的期待正不断推动技术边界。一个看似简单的对话——你说一句&#xff0c;数字人立刻回应并同步口型表情——背后…

作者头像 李华
网站建设 2026/1/10 2:05:13

Linly-Talker开源项目上手难吗?新手必看入门手册

Linly-Talker开源项目上手难吗&#xff1f;新手必看入门手册 在虚拟主播、AI客服、数字员工这些曾经只存在于科幻电影中的角色&#xff0c;正以惊人的速度走进我们的现实生活。而支撑这一切的&#xff0c;不再是动辄百万预算的专业动画团队&#xff0c;而是一套高度集成的AI系统…

作者头像 李华
网站建设 2025/12/21 0:45:23

Linly-Talker语音语调可控:支持愤怒、温柔等语气调节

Linly-Talker&#xff1a;让数字人“有情绪”地说话 在直播间里&#xff0c;虚拟主播声情并茂地讲解产品&#xff0c;语气时而激昂、时而温柔&#xff1b;在心理陪伴应用中&#xff0c;AI角色用低缓柔和的声音安慰用户&#xff1b;在在线课堂上&#xff0c;数字教师以鼓励的语调…

作者头像 李华