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 中的内容。这样就满足了题目的要求。

相关推荐
Evand J20 分钟前
【MATLAB例程】到达角度定位(AOA),平面环境多锚点定位(自适应基站数量),动态轨迹使用EKF滤波优化。附代码下载链接
开发语言·matlab·平面·滤波·aoa·到达角度
细节控菜鸡44 分钟前
【2025最新】ArcGIS for JS 实现随着时间变化而变化的热力图
开发语言·javascript·arcgis
Pluto_CSND1 小时前
Java实现gRPC双向流通信
java·开发语言·单元测试
原来是猿2 小时前
谈谈环境变量
java·开发语言
应用市场2 小时前
本地局域网邮件管理系统:从原理到实现的完整指南
开发语言
Tony Bai2 小时前
【Go 网络编程全解】12 本地高速公路:Unix 域套接字与网络设备信息
开发语言·网络·后端·golang·unix
oioihoii2 小时前
深入理解 C++ 现代类型推导:从 auto 到 decltype 与完美转发
java·开发语言·c++
报错小能手3 小时前
项目——基于C/S架构的预约系统平台 (1)
开发语言·c++·笔记·学习·架构
MYX_3093 小时前
第四章 多层感知机
开发语言·python
彬彬醤3 小时前
如何正确选择住宅IP?解析适配跨境、流媒体的网络工具
服务器·开发语言·网络·网络协议·tcp/ip