【C语言】深入解开指针(二)

  🌈write in front :

🔍个人主页 : @啊森要自信的主页

🌈作者寄语 🌈: 小菜鸟的力量不在于它的体型,而在于它内心的勇气和无限的潜能,只要你有决心,就没有什么事情是不可能的。

欢迎大家关注🔍点赞👍收藏⭐️留言📝>希望看完我的文章对你有小小的帮助,如有错误,可以指出,让我们一起探讨学习交流,一起加油鸭。 请添加图片描述

文章目录

  • 一、const修饰指针
    • 1.1 const修饰变量
    • 1.2 const修饰指针变量
  • 二、指针运算
    • 2.1 指针+ - 整数
    • 2.2 指针-指针
    • 2.3 指针的关系运算
  • 三、野指针
    • 3.1 野指针成因
    • 3.2 如何规避野指针
  • 四、 assert断⾔
  • 总结


一、const修饰指针

1.1 const修饰变量

变量是可以修改的,如果把变量的地址交给⼀个指针变量,通过指针变量的也可以修改这个变量。但是如果我们希望⼀个变量加上⼀些限制,不能被修改,怎么做呢?

这就是const的作⽤。

#include <stdio.h>
int main()
{
	int m = 0;
	m = 66;//m是可以修改的
	const int n = 0;
	n = 88;//n是不能被修改的
	return 0;
}

上述代码中n是不能被修改的,其实n本质是变量,只不过被const修饰后,在语法上加了限制,只要我们在代码中对n就⾏修改,就不符合语法规则,就报错,致使没法直接修改n
在这里插入图片描述

但是如果我们绕过n,使⽤n的地址,去修改n就能做到了,虽然这样做是在打破语法规则,但是这样做到底可不可以呢?让我们来验证一下:

# define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
	const int n = 0;
	printf("n = %d\n", n);
	int* p = &n;
	*p = 666;
	printf("n = %d\n", n);
	return 0;
}

我们可以看到这⾥⼀个确实修改了,但是我们还是要思考⼀下,为什么n要被const修饰呢?就是为了不能被修改,如果p拿到n的地址就能修改n,这样就打破了const的限制,这是不合理的,所以应该让p拿到n的地址也不能修改n,那接下来怎么做呢?
在这里插入图片描述

1.2 const修饰指针变量

  1. const放在 * 的左边,限制是*p意思是不能通过指针变量p修改p指向的空间的内容:
    *p = 20//错误
    但是是不受限制的
    p = &b;//正确
    在这里插入图片描述

2. const 放在 * 的右边,限制是p变量,也就是p变量不能被修改了,没办法再指向其他变量了
p = &b;//错误
但是*p不受限制,还是可以通过p来修改p所指向的对象的内容
*p = 20;//正确


在这里插入图片描述

  1. const放在*两边,p*p都限制住了
    在这里插入图片描述
    在这里插入图片描述

结论:const修饰指针变量的时候
• const 如果放在*左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。但是指针变量本⾝的内容可变。
• const如果放在*右边,修饰的是指针变量本⾝,保证了指针变量的内容不能修改,但是指针指向的内容,可以通过指针改变。

二、指针运算

指针的基本运算有三种,分别是:
• 指针± 整数
• 指针-指针
• 指针的关系运算

2.1 指针+ - 整数

因为数组在内存中是连续存放的,只要知道第⼀个元素的地址,顺藤摸⽠就能找到后⾯的所有元素。
在这里插入图片描述

#include <stdio.h>
//指针+- 整数
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p = &arr[0];
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));//p+i 这⾥就是指针+整数
	}
	return 0;
}

在这里插入图片描述

2.2 指针-指针

//指针-指针
#include <stdio.h>
int strl(char* s)
{
	char* pa = s;
	while (*pa != '\0')
		pa++;
	return pa - s;
}
int main()
{
	printf("%d\n", strl("abcef"));
	return 0;
}

字符串"abcdef\0"以\0为结尾,作为结束标志。
在这里插入图片描述

2.3 指针的关系运算

//指针的关系运算
#include <stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* pa = &arr[0];
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	while (pa < arr + sz) //指针的⼤⼩⽐较
	{
		printf("%d ", *pa);
		pa++;
	}
	return 0;
}

在这里插入图片描述

三、野指针

在 C 语言中,野指针是指未被初始化的指针。野指针可能指向内存中不存在(随机的、不正确的、没有明确限制的)的数据,也可能指向已被释放的内存。

3.1 野指针成因

  1. 指针未初始化

在这里插入图片描述

  1. 指针越界访问

在这里插入图片描述
2. 指针指向的空间释放

在C语言中,当一个指针指向一个函数中分配的内存空间时,如果在该函数返回之前释放了该内存空间,那么这个指针就成为了一个野指针这是因为在函数返回后,该内存空间已经被释放,指针再次访问这个空间就会导致未定义的行为。

因此,当你使用指针指向调用函数的空间时,你应该确保在函数返回之前不要释放这个内存空间。

建议:如果你需要在函数外部访问这个空间,你应该将其复制到一个新的内存空间中,并在函数返回之前释放原始内存空间。

在这里插入图片描述
函数test()返回了一个指向局部变量的指针。当函数test()执行完毕后,它的局部变量a的内存空间会被释放。因此,返回的指针指向的内存空间已经无效了。在这种情况下,pa是一个野指针,因为它指向的内存空间已经不再有效。

在实际运行中,尽管这些代码可能不会立即导致错误,但它们会导致未定义的行为。由于释放的内存空间可能被其他变量或函数使用,因此在这种情况下,pa可能会包含无法预测的值,或者程序可能会崩溃。因此,虽然这些代码可能不会立即报错,但它们是不安全的,并且可能导致程序出现问题

3.2 如何规避野指针

  1. 如果明确知道指针指向哪⾥就直接赋值地址,如果不知道指针应该指向哪⾥,可以给指针赋值NULL.
    NULL 是C语⾔中定义的⼀个标识符常量,值是0,0也是地址,这个地址是⽆法使⽤的,读写该地址
    会报错。
	#ifdef __cplusplus
	 	#define NULL 0
 	#else
		 #define NULL ((void *)0)
 #endif

在这里插入图片描述

  1. ⼩⼼指针越界
    ⼀个程序向内存申请了哪些空间,通过指针也就只能访问哪些空间,不能超出范围访问,超出了就是越界访问
  2. 指针变量不再使⽤时,及时置NULL,指针使⽤之前检查有效性
  3. 避免返回局部变量的地址

四、 assert断⾔

assert.h 头⽂件定义了宏 assert() ,⽤于在运⾏时确保程序符合指定条件,如果不符合,就报错终⽌运⾏。这个宏常常被称为“断⾔”。

assert(p != NULL);

p 是否等于 NULL 。如果确实不等于 NULL ,程序继续运⾏,否则就会终⽌运⾏,并且给出报错信息提⽰。

assert()宏接受一个表达式作为参数。如果该表达式为真(返回值非零),assert()不会产生任何作用,程序继续运行。如果该表达式为假(返回值为零),assert()就会报错,在标准错误流stderr中写入一条错误信息,显示没有通过的表达式,以及包含这个表达式的文件名和行号。

#include <stdio.h>
#include <assert.h>

int divide(int a, int b) {
    assert(b != 0);  // 断言:除数不能为0
    return a / b;
}

int main() {
    int a = 10;
    int b = 0;

    int result = divide(a, b);
    printf("Result: %d\n", result);

    return 0;
}

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

#define NDEBUG
#include <assert.h>

assert() 的一个缺点是:由于引入了额外的检查,增加了程序的运行时间。在 Debug 中使用,在 Release 版本中选择禁用 assert 就可以,在像VS这样的集成开发环境中,在 Release 版本中,直接就被优化掉了。这样在debug版本中有利于程序员排查问题,在 Release 版本中不影响用户使用时程序的效率。


总结

通过本章我们学习了const修饰变量和指针变量,指针的+ -整数,指针-指针和指针关系运算,以及野指针的形成的原因,怎么去预防野指针。如果你觉得我的文章对你的有小小的帮助,可以给予博主一个小小的赞,感谢您的观看!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/142283.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Hubspot是如何发展到今天的?有哪些实用工具?

HubSpot&#xff0c;作为一家全球领先的数字化市场营销和销售平台提供商&#xff0c;通过其强大的生态圈和创新的解决方案&#xff0c;帮助企业实现高效运营、客户吸引和业务增长。运营坛今天将详细介绍HubSpot的发展历程以及其三大核心产品&#xff1a;CMS Hub、Marketing Hub…

雷达波形及MATLAB仿真

文章目录 前言一、雷达波形二、Matlab 仿真1、SFW 的距离分辨率和距离模糊①、MATLAB 源码②、仿真结果 三、资源自取 前言 本文对雷达波形的内容以思维导图的形式呈现&#xff0c;有关仿真部分进行了讲解实现。 一、雷达波形 思维导图如下图所示&#xff0c;如有需求请到文章…

No198.精选前端面试题,享受每天的挑战和学习

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云课上架的前后端实战课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入…

@Async注解的坑

问题描述 一个方法调用另一个方法(该方法使用Async注解)在同一个类文件中&#xff0c;该注解会失效&#xff01; 问题复现 TestAsyncController 类 import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Async; im…

社区新零售:改变生活方式的创新商业模式

社区新零售&#xff1a;改变生活方式的创新商业模式 社区新零售&#xff0c;顾名思义&#xff0c;以社区为核心&#xff0c;利用互联网、大数据、人工智能等先进技术&#xff0c;将线上购物和线下体验有机结合&#xff0c;形成一种全新的零售模式。它特别强调地理位置的便利性&…

2023亚太杯数学建模C题思路解析

文章目录 0 赛题思路1 竞赛信息2 竞赛时间3 建模常见问题类型3.1 分类问题3.2 优化问题3.3 预测问题3.4 评价问题 4 建模资料5 最后 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 竞赛信息 2023年第十三…

【Axure高保真原型】3D饼图_移入显示数据标签

今天和大家分享3D饼图_移入显示数据标签的原型模板&#xff0c;鼠标移入扇形区域时&#xff0c;对应区域会变绿&#xff0c;可以查看该区域对应的项目、数据和占比&#xff0c;这个原型模板是用Axure原生元件制作的&#xff0c;所以无需联网&#xff0c;而且可以自由修改样式、…

windows服务器热备、负载均衡配置

安装网络负载平衡 需要加入的服务器上全部需要安装网络负载平衡管理器 图形化安装&#xff1a;使用服务器管理器安装 在服务器管理器中&#xff0c;使用“添加角色和功能”向导添加网络负载均衡功能。 完成向导后&#xff0c;将安装 NLB&#xff0c;并且不需要重启计算机。 …

HarmonyOS开发(二):TypeScript入门

1、编程语言介绍 ArkTS是HarmonyOS主推的应用开发语言&#xff0c;它是在TypeScript语言的基础之上&#xff0c;匹配ArkUI框架&#xff0c;扩展了声明式UI、状态管理等相应的能力&#xff0c;让开发者以更简洁、更自然的方式开发跨端应用。 ArkTS、TypeScript和JavaScript之间…

利用Python群组分析方法剖析客户行为

大家好&#xff0c;如今的企业有能力收集大量的数据&#xff0c;这些数据可以帮助企业制定更好的策略并了解其客户的行为。Cohort分析可以在其中发挥作用&#xff0c;Cohort分析是一种了解客户行为或用户互动的强大工具&#xff0c;并为企业提供有价值的见解&#xff0c;本文中…

竞赛选题 深度学习的动物识别

文章目录 0 前言1 背景2 算法原理2.1 动物识别方法概况2.2 常用的网络模型2.2.1 B-CNN2.2.2 SSD 3 SSD动物目标检测流程4 实现效果5 部分相关代码5.1 数据预处理5.2 构建卷积神经网络5.3 tensorflow计算图可视化5.4 网络模型训练5.5 对猫狗图像进行2分类 6 最后 0 前言 &#…

Python--集合----无序,去重,空集合只能用set()方法

集合&#xff08;set&#xff09;是一个无序的不重复元素序列。 特点&#xff1a;天生去重 无序 集合定义&#xff1a;在Python中&#xff0c;我们可以使用一对花括号 {} 或者 set()方法 来定义集合&#xff0c; 但是如果你 定义的集合是一个 空集合&#xf…

C++阶段复习‘‘‘‘总结?【4w字。。。】

文章目录 前言类和对象C类定义和对象定义类成员函数C 类访问修饰符公有&#xff08;public&#xff09;成员私有&#xff08;private&#xff09;成员受保护&#xff08;protected&#xff09;成员 继承中的特点类的构造函数和析构函数 友元函数内联函数this指针指向类的指针类…

什么是会话固定以及如何在 Node.js 中防止它

什么是会话固定以及如何在 Node.js 中防止它 在深入讨论之前&#xff0c;我们需要了解会话是什么以及会话身份验证如何工作。 什么是会话&#xff1f; 正如我们所知&#xff0c;HTTP 请求是无状态的&#xff0c;这意味着当我们发送登录请求时&#xff0c;并且我们有有效的用…

Linux系统编程——标准c库对文件操作

Linux、标准c库对文件操作的区别 1.来源 从来源的角度看&#xff0c;两者能很好的区分开&#xff0c;这也是两者最显而易见的区别: open是UNIX系统调用函数 (包括LINUX等) &#xff0c;返回的是文件描述符 (File Descriptor)&#xff0c;它是文件在文件描述符表里的索引。.f…

腾讯云5年云服务器还有吗?腾讯云5年时长服务器入口在哪?

如果你是一名企业家或者是一个热衷于数字化转型的创业者&#xff0c;那么腾讯云最近推出的一项优惠活动绝对不会让你无动于衷。现在&#xff0c;腾讯云正在大力推广一项5年特价云服务器活动&#xff0c;只需要花费3879元&#xff0c;你就可以享受到腾讯云提供的优质服务。 腾讯…

RK3568平台开发系列讲解(Linux系统篇)Linux内核定时器详解

🚀返回专栏总目录 文章目录 一、系统节拍率二、内核定时器简介三、内核定时器API四、延时函数沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 Linux 内核中有大量的函数需要时间管理,比如周期性的调度程序、延时程序、对于我们驱动编写者来说最常用的定时器。硬件定…

Direct3D拾取

假设在屏幕上单击,击中的位置为点s=(x,y)。由图可以看出,用户选中了茶壶。但是仅给出点s,应用程序还无法立即判断出茶壶是否被选中。所以针对这类问题,我们需要采用一项称为“拾 取(Picking)”的技术。 茶壶和屏幕点s之间的一种联系是茶壶被投影到了一个包含了s的区域中。…

【QT系列教程】之一安装配置

文章目录 一、qt简介二、qt下载2.1、官网地址&#xff1a;https://www.qt.io/2.2、下载2.3、https://download.qt.io/official_releases/online_installers/ 三、qt安装3.1、登录账号&#xff0c;没有账号自己注册3.2、我是个人安装&#xff0c;如果是公司填写公司名字3.3、欢迎…

文件批量重命名001开始,怎么操作?

如何让文件批量重命名001开始&#xff1f;对于那些需要批量重命名文件的用户来说&#xff0c;这项任务可能涉及到整理照片、音乐文件或者调整电子文档的命名&#xff0c;而这些工作都可能会相当繁琐。有时候&#xff0c;我们希望以数字顺序名称从"001"开始批量修改文…