当前位置: 首页 > news >正文

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

1. const 修饰指针

1.1 const 修饰变量

        变量是可以修改的,但当我们想加上一些限制使某个变量不能修改,这时就会用到 const。

        我们可以看到,当 x 被 const 修饰后,再修改 x 时会发生错误。要想解决这个问题,我们可以通过指针来绕开,通过访问 x 地址的方式来间接修改 x 的值,如下图:

        这里用 const 修饰的 x 叫常变量,表示 x 的本质还是变量,但是它不能被修改。上图中我们用 const 就是为了使值不被修改,这样就打破了 const 的限制,,所以我们应该让 p 拿到 x 的地址也不能修改 x。

1.2 const 修饰指针变量、

        const 修饰指针变量时,既可以放到 * 的左边(const int * x 或 int const * x),也可以放到 * 的右边(int * const x),但它们的意义不一样。

        

        从上图我们可以看到如下结论:

        const 如果放到 * 的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变,而指针变量本身的内容可以改变(也就是指针中存的地址);

        const 如果放到 * 的右边,修饰的是指针变量本身,保证了指针变量的内容不能修改(也就是指针中存的地址),但是指针指向的内容可以通过指针来改变;

        不仅如此,* 左右两边也可以同时加上 const 来修饰,这样指针指向的内容和指针本身都不能修改。

2. 野指针

        野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)。

2.1 野指针的成因

        1.指针位初始化:

#include <stdio.h>
int main()
{int *x;//局部变量指针x未初始化,默认为随机值*x = 20;return 0;
}

        2.指针越界访问:

#include <stdio.h>
int main()
{int arr[10] = {2};int* p = &arr[0];for(int i = 0;i <= 10;i++){*p(++) = 2;//此时循环了11次,超过了 arr 的范围,p就是野指针}return 0;
}

        3.指针指向的空间释放

#include <stdio.h>
int* csdn()
{int x = 3;return &x;
}
int main()
{int* p = csdn();//当返回主函数时 x 的空间被释放,此时p变为野指针return 0;
}

2.2 如何规避

        我们预防野指针的出现时,同样的,首先要对指针进行初始化,若我们开始不知道指针应该指向哪里,可以对指针赋值 NULL 这样,这个地址是无法使用的,读写改地址时会报错;其次,编写代码时要时刻检查代码,防止指针越界;最后,指针不再使用时将其及时置 NULL 因为约定俗成的⼀个规则就是:只要是NULL指针就不去访问, 同时使用指针之前可以判断指针是否为NULL。还要注意不要返回局部变量的地址。

3. assert 断言

        assert.h 头⽂件定义了宏 assert() ,⽤于在运行时确保程序符合指定条件,如果不符合,就报错终止运行。这个宏常常被称为“断⾔”。例如:assert (p != NULLL) 当代码运行到这一段时,验证变量 p 是否等于 NULL,若果不等于则程序继续执行,若等于则终止运行并给出报错信息提示。

        assert() 的使⽤对程序员是非常友好的,使用 assert() 有几个好处:它能自动标出文件和出问题的行号。还有⼀种⽆需更改代码就能开启或关闭 assert() 的机制:如果已经确认程序没有问题,不需要再做断言,就在 #include <assert.h>语句的前面,定义⼀个宏 NDEBUG。

#define NDEBUG
#include <assert.h>

        assert() 的缺点是,因为引⼊了额外的检查,增加了程序的运⾏时间。

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

4.1 strlen的模拟实现

        库函数 strlen 的功能是求字符串⻓度,统计的是字符串中 \0 之前的字符的个数。函数原型如下: size_t strlen ( const char * str );

        参考代码如下:

4.2 传值调用和传址调用

        学习指针后到底有什么作用呢?比如我们来写一个函数:交换两个整型变量的值。

        再没学习指针之前我们可能会写出这样的代码:

        我们发现这样的代码并没有完成任务,因为调用函数时形参 x 和 y 都分别建立了各自的空间,交换值时是在 x 和 y 的空间中交换的,与 a 和 b 没有关系,所以没有交换掉 a 和 b 的值。

        但是当我们用指针时就能很好的解决这个问题;

        我们用指针时指针变量 x 和 y 分别直接指向了 a 和 b 的空间,x和y中分别存放的是 a 和 b 的地址,再通过解引用操作符 * 就能直接访问 a 和 b 中的内容。这样就满足了题目的要求。

http://www.dtcms.com/a/289374.html

相关文章:

  • FreeRTOS学习笔记之内存管理
  • Spring MVC上下文容器在Web容器中是如何启动的(源码深入剖析)?
  • LeetCode 121. 买卖股票的最佳时机
  • Docker安装mysql、redis
  • 学习日志15 python
  • 深入理解Graphite协议:数据采集、存储与可视化的核心技术
  • [硬件电路-57]:根据电子元器件的受控程度,可以把电子元器件分为:不受控、半受控、完全受控三种大类
  • 65-OVP保护电路
  • 医学图像超分辨率重建深度学习模型开发报告
  • [硬件电路-58]:根据电子元器件的控制信号的类型分为:电平控制型和脉冲控制型两大类。
  • FNAF同人:简陋的测试
  • Pact 合约测试框架
  • 民法学学习笔记(个人向) Part.4
  • 20250720-5-Kubernetes 调度-污点与污点容忍_笔记
  • 力扣(LeetCode)第 459 场周赛
  • pthread_detach与pthread_join区别及使用场景
  • MySQL EXPLAIN 解读
  • 奥比中光双目摄像头实现物品抓取的机器人系统
  • 算法-递推
  • golang踩坑之url不会decode问题
  • 物联网安装调试-继电器
  • Google-多代理设计:用更好的提示和拓扑优化代理
  • 可视化技术如何拯救柔性生产?小批量定制化订单的排产仿真与产能透视
  • Navicat Premium:一站式数据库管理解决方案
  • Codeforces Round 1037 (Div. 3)(A,B,C,D,E,F,G1)
  • Centos卷挂载失败系统无法启动
  • 力扣:动态规划java
  • 《剥开洋葱看中间件:Node.js请求处理效率与错误控制的深层逻辑》
  • 深度学习篇---矩阵
  • (保姆级)Windows11安装GPU版本Pytorch2.3、CUDA12.6