news 2026/8/19 22:06:35

深挖C语言:深入理解指针(2)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
深挖C语言:深入理解指针(2)

目录

1.const修饰指针

2.野指针

2.1 野指针的成因:

2.2 规避野指针

3.assert断言

4.指针的使用和传值调用

4.1 库函数strlen的实现

4.2 传值调用和传址调用

1.const修饰指针

作用:使操作对象不能被随意修改

操作对象:普通变量,指针,函数形参,全局常量。

int m = 10; m = 20; // m 能被修改 const int n = 20; n = 10;// n 不能被修改

这里虽然 n 被 const 修饰,强行修改会报错,但是我们可以通过指针的方式绕过const的限制修改变量n,代码如下:

const int n = 10; int* pn = &n; *pn = 20;// n 成功被修改

这种行为显然打破了语法规则,如果我们不想让变量通过指针被修改,那么就对指针也进行 const 修饰,代码如下:

const int n = 10; const int* pn = &n; * pn = 20;// n不能被修改,代码报错 int* const pn = &n; *pn = 20;// n可以被修改

虽然说对指针变量进行了const修饰,但是const放在 * 的前面或者后面,修饰的对象不相同,所加的范围限制也不相同。

const int n = 10; int m = 20; int* const pn = &n; *pn = 30;//没问题 pn = &m;//报错,指针变量pn不能被修改 const int* pm = &m; *pm = 30;//报错,*pm不能被修改 pm = &n;//没问题

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

1.如果放在 * 的左边,修饰的是所存指针指向的内容,使指向的内容不能被修改。

2.如果放在 * 的右边,修饰的是指针变量所存的内容,指针变量的内容不能被修改

2.野指针

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

2.1 野指针的成因:

(1)指针未初始化

int n; int* pn = &n;// pn因变量未初始化成为野指针

(2)指针越界访问

int arr[10] = { 0 }; int* parr = &arr[10];// parr越界访问成为野指针

(3)指针指向的空间被释放

int* test() { int n = 100; return &n; } int main() { int* p = test();// n的空间被释放,p成为野指针 printf("%d\n",*p); return 0; }

注意:一个程序向内存申请了哪些空间,通过指针也就只能访问这些空间,超出这个范围了就是越界访问。

2.2 规避野指针

(1)指针使用之前检查指针的有效性,指针变量不再使用时,及时置NULL

(2)不要返回局部变量的地址。

野指针就好比是一条没有拴绳的野狗,对其放任不管是很危险的,对指针及时赋予NULL,相当于把这条野狗给栓了起来,对NULL约定俗成的一个规矩就是:只要是NULL指针就不去访问,规避了野指针的形成。

3.assert断言

使用代码如下:

#include<assert.h> //使用assert不要忘记带上头文件 assert(p != NULL);

代码运行时,assert()宏接受一个表达式作为参数,如果该表达式为真(返回结果非零),assert()就不会产生任何作用,程序继续执行。如果表达式为假(返回结果为零),assert()就会中止代码的运行,并进行报错

如果已经确定程序没有任何问题,不需要再进行断言,此时在#include<assert.h>前定义一个宏NDEBUG即可,代码如下:

#define NDEBUG //消除assert() #include<assert.h>

注意:(1)assert因为对程序有了检查,会增加程序运行的时间

(2)assert一般是在Debug中使用在Release中选择禁用assert,这样在Debug版本中有利于程序员排查问题,在Release版本中不影响用户使用程序的效率。

4.指针的使用和传值调用

4.1 库函数strlen的实现

作用:求字符串的长度。代码如下:

int my_strlen(const char * str) { int count = 0; assert(str); while(*str) { count++; str++; } return count; } int main() { int len = my_strlen("abcdef"); printf("%d\n",len); return 0; }

4.2 传值调用和传址调用

为了区分传值调用和传址调用,简写交换整型变量的函数便于理解:

传值调用代码:

#include<stdio.h> void Swap1(int x,int y) { int tmp = x; x = y; y = tmp; } int main() { int a = 10; int b = 20; Swap1(a,b); printf("%d %d",a,b);//a,b并未被交换 return 0;

传址调用代码:

#include<stdio.h> void Swap2(int* x,int* y) { int tmp = *x; *x = *y; *y = tmp; } int main() { int a = 10; int b = 20; Swap2(&a,&b); printf("%d %d",a,b);//a,b的值成功被交换 return 0; }

由此我们可知,实参传递给形参的时候,形参会单独创建一份临时空间变量来接受实参,对形参的修改不影响实参。

这是Swap1失败的原因,在上述函数中,只能使用传址调用。

结论:传址调用可以让函数和主调函数之间建立真正的联系,在函数内部可以修改主调函数的变量;所以函数只需要主调函数中的变量值来进行计算,就可以使用传值调用;如果函数内部要修改主调函数中的变量值,就需要使用传址调用。

最后,感谢阅读,欢迎大家交流与指正。

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

线性可调双路输出电源:原理、设计与噪声抑制全解析

1. 线性可调双路输出电源&#xff1a;为何它仍是工程师的“心头好”&#xff1f; 在如今这个开关电源&#xff08;SMPS&#xff09;大行其道的时代&#xff0c;提起“线性电源”&#xff0c;很多刚入行的朋友可能会觉得它有点“老古董”——效率不高、体积不小、发热还大。但如…

作者头像 李华
网站建设 2026/8/19 22:02:53

OptiCommPy模拟光马赫-曾德尔调制器

文章目录基本原理mzm基本原理 光马赫-曾德尔调制器&#xff08;Optical Mach-Zehnder Modulator, MZM&#xff09;可将相位变化转化为光强变化&#xff0c;是现代高速光通信中最核心的光开关和幅度调制器。 MZM的物理结构实际上是个干涉仪&#xff0c;其工作流程为 分光&…

作者头像 李华
网站建设 2026/8/19 22:01:03

CAD绘图效率提升:从练习图29拆解系统绘图流程与高效命令组合

如果你是一名机械工程师、建筑设计师&#xff0c;或者正在学习CAD制图&#xff0c;那么你一定遇到过这样的困境&#xff1a; 明明软件操作都会&#xff0c;但面对一个复杂的零件图或建筑平面图&#xff0c;却不知从何下手&#xff0c;画得又慢又容易出错。 这背后真正的问题…

作者头像 李华
网站建设 2026/8/19 21:57:59

snpe-VGG案例(uv环境-全流程 教程)

摘要&#xff1a;本文详细记录了在 Linux 主机上使用 Qualcomm SNPE SDK 2.21 部署 VGG16 ONNX 模型的完整流程&#xff0c;涵盖环境搭建、依赖安装、模型下载与转换、主机 CPU 推理验证、ARM64 交叉编译 C Sample、板端部署运行以及输出结果验证等九个步骤。文章还针对官方案例…

作者头像 李华
网站建设 2026/8/19 21:57:54

实测通勤15分钟出片全流程,不用电脑不用大内存手机

很多人不敢相信&#xff0c;在微信里用小程序就能完成一整条推文视频的配音&#xff0c;我们拿一段3000字的男频爽文来实测&#xff0c;全程在通勤的地铁上操作&#xff0c;15分钟就能跑完所有流程&#xff1a;第1分钟&#xff1a;打开微信&#xff0c;搜索媒小叁配音小程序&am…

作者头像 李华