C语言深入理解指针三(16)


前言

  我们来继续学习指针!


一、assert断言

assert.h头文件定义了宏assert(),用于在运行时确保程序符合指定条件,如果不符合,就报错终止运行,这个宏常常被称为“断言”

assert(p != NULL);

上面代码在程序运行到这一行语句时,验证变量p是否等于NULL,如果确实不等于NULL,程序继续运行,否则就会终止运行,并且给出报错信息提示

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

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

#define NDBUG
#include <assert.h>
然后,重新编译程序,编译器就会禁用文件中所有的assert()语句,如果程序又出现问题,可以移除这条指令,再次编译,就又启用了assert()语句

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

二、指针的使用和传址调用

strlen的模拟实现

库函数strlen的功能是求字符串的长度,统计的是字符串中\0之前的字符的个数
函数原型如下:

size_t strlen(const char* str);

参数str接收一个字符串的起始地址,然后开始统计字符串中\0之前的字符个数,最终返回长度
如果要模拟实现只要从起始地址开始向后逐个字符的遍历,只要不是\0字符,计数器就+1,这样知道\0就停止
在这里插入图片描述

在这里,my_strlen(arr)就是传址调用

传值调用和传址调用

学习指针的目的就是使用指针解决问题,可是有什么问题,是非指针不可的呢?

写一个函数,作用是交换两个变量的值

下面是第一种方案:在这里插入图片描述
可以发现,这种方法失败了,这是为什么呢?

原因在这:
在这里插入图片描述
a,b变量有自己的地址,x,y变量也有自己的地址,也就是说传值调用函数时函数的实参传递给形参时,形参是实参的一份临时拷贝,有自己独立的空间,对形参的修改不会影响实参

也就是说,如果真要修改实参的话,应该使用传址调用
Swap(&a, &b);

传址调用,可以让函数和主调函数之间建立真正的联系,在函数内部可以修改主调函数中的变量,所以未来函数中只是需要主调函数中的变量值来实现计算,就可以采用传值调用,如果函数内部要修改主调函数中的变量的值,就需要传址调用

三、数组名的理解

在之前我们就有使用指针访问数组的内容

int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = &arr[0];

这里我们使用&arr[0]的方式拿到了数组第一个元素的地址,但是其实数组名本来就是地址,而且是数组首元素的地址,我们来做个测试吧!
在这里插入图片描述
输出结果如下:
在这里插入图片描述

显然,我说的没错,数组名就是数组首元素的地址,请再次牢记!

另外,我还想要大家记住两个例外结论:
1.sizeof内部单独放一个数组名的时候,数组名表示的是整个数组,计算的是整个数组的大小,单位是字节
2.&数组名,数组名表示的是整个数组,取出的是整个数组的地址
除此之外,遇到的所有数组名都是数组首元素的地址

首元素的地址和整个数组的地址的区别:
在这里插入图片描述
虽然两者从值来说是一样的,但是两者差了不少,后面会慢慢展示
&arr 和 &arr + 1差了0x28个字节,(2 * 16 + 8 = 40),也就是是10个int的大小

四、使用指针访问数组

有了前面的知识,又结合数组的特点,我们就可以很方便的使用指针来访问数组了
在这里插入图片描述

插个有意思的,上图中你可以看出来其实arr[i] 跟 *(arr + i)其实是等价的
这说明了这个数组下标引用符[]没那么神奇,实际上,程序跑起来的时候,底层上前者也会被编译器转换为后者
又因为 *(arr + i) 与 *(i + arr)从结果来看是没差的
所以有一个大胆的想法,arr[i]与i[arr]也是等价的,实践后发现果然如此:
在这里插入图片描述
勤动脑,是不会错的

五、一维数组传参的本质

  数组我们学过了,之前也讲了,数组是可以传递给函数的,于是我们来讨论一下数组传参的本质
  首先从一个问题开始,我们之前都是在函数外部计算数组的元素个数,那我们可以把数组传给一个函数后,函数内部求数组的元素个数吗?
在这里插入图片描述
这个程序的输出结果很有意思,sz1输出10,sz2输出1
其实我们想,如果想单个形参变量一样拷贝实参,那么数组形参就要拷贝一个数组,这太占用内存了
所以,其实我们传递的是数组首元素的地址,而形参部分int arr[],其实本质上是int* arr

六、冒泡排序

  排序的方法有很多种,有时候你不得不感概计算机前辈的智慧,我后期会出一篇文章来讲解八大排序
  冒泡排序的核心思想就是:两两相邻的元素进行比较

冒泡排序的核心逻辑是,假如一个数组arr有n个元素,那么第一趟遍历 1 ~ n 个元素,两两相比,小的往左移动,这样最大的元素就变成了第 n 个元素;第二趟遍历 1 ~ n - 1 个元素,同样的,第二大的元素就变成了第 n - 1 个元素,直至排完

其实思路还是蛮简单的,这时候我们就要开始设计,首先每次排好一个元素,一共有 n 个元素,一共需要排 n - 1 次,因为最后一次,剩下最后一个元素就是停在正确的位置上,所以我们需要一个 for 循环 ;而对于第 i 趟,我们要从第2个元素移动到第 n - i + 1 个元素 (arr[i] 与 arr[i - 1])
思路明确,代码自然而然地就写出来了,要对自己有自信:
在这里插入图片描述
这时候,我们再考虑C语言的模块化和结构化的特点,封装两个函数BubbleSort和Print,这时候,也就是相当于传址调用了

甚至还可以来点小优化,比如说某一趟走完发现都不需要移动,说明此时已经排完序了,直接跳出来即可
这要怎么实现?就交给大家了!

在这里插入图片描述

这是C的哲学,也是C的美感

七、二级指针

指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里?
这就是二级指针
在这里插入图片描述
对于二级指针的运算有:

  1. *ppa 通过对 ppa 中的地址进行解引用,这样找到的是pa, *ppa 其实访问的是pa
  2. **ppa 先通过*ppa找到pa,然后对pa进行解引用操作:*pa,那找到的是a

其实类比一下,也有三级指针,只是二级指针都用的较少,更别提三级指针了

八、指针数组

何为指针数组?

指针数组是指针还是数组?

还是以类比作为理解方式,我们想整型数组是什么?就是存放整型的数组,字符数组是存放字符的数组
这样想的话,那么指针数组就是存放指针的数组,每个元素都是同类型的指针
在这里插入图片描述

用指针数组模拟二维数组

注意,这是模拟出二维数组的效果,但是不是二维数组!
在这里插入图片描述
在这里插入图片描述
你会发现,之所以说是模拟,其实是因为parr所指向的每个int*所相当的一维数组是不连续
在这里插入图片描述


总结

  感觉如何,从本章开始,大家是不是感觉难度上来了呢,加油,后面会更精彩的!

使用优化算法,以优化VMD算法的惩罚因子惩罚因子 (α) 和分解层数 (K)。 1、将量子粒子群优化(QPSO)算法与变分模态分解(VMD)算法结合 VMD算法背景: VMD算法是一种自适应信号分解算法,主要用于分解信号为不同频率带宽的模态。 VMD的关键参数包括: 惩罚因子 α:控制带宽的限制。 分解层数 K:决定分解出的模态数。 QPSO算法背景: 量子粒子群优化(QPSO)是一种基于粒子群优化(PSO)的一种改进算法,通过量子行为模型增强全局搜索能力。 QPSO通过粒子的量子行为使其在搜索空间中不受位置限制,从而提高算法的收敛速度与全局优化能力。 任务: 使用QPSO优化VMD中的惩罚因子 α 和分解层数 K,以获得信号分解的最佳效果。 计划: 定义适应度函数:适应度函数根据VMD分解的效果来定义,通常使用重构信号的误差(例如均方误差、交叉熵等)来衡量分解的质量。 初始化QPSO粒子:定义粒子的位置和速度,表示 α 和 K 两个参数。初始化时需要在一个合理的范围内为每个粒子分配初始位置。 执行VMD分解:对每一组 α 和 K 参数,运行VMD算法分解信号。 更新QPSO粒子:使用QPSO算法更新粒子的状态,根据适应度函数调整粒子的搜索方向和位置。 迭代求解:重复QPSO的粒子更新步骤,直到满足终止条件(如适应度函数达到设定阈值,或最大迭代次数)。 输出优化结果:最终,QPSO算法会返回一个优化的 α 和 K,从而使VMD分解效果最佳。 2、将极光粒子(PLO)算法与变分模态分解(VMD)算法结合 PLO的优点与适用性 强大的全局搜索能力:PLO通过模拟极光粒子的运动,能够更高效地探索复杂的多峰优化问题,避免陷入局部最优。 鲁棒性强:PLO在面对高维、多模态问题时有较好的适应性,因此适合海上风电时间序列这种非线性、多噪声的数据。 应用场景:PLO适合用于优化VMD参数(α 和 K),并将其用于风电时间序列的预测任务。 进一步优化的建议 a. 实现更细致的PLO更新策略,优化极光粒子的运动模型。 b. 将PLO优化后的VMD应用于真实的海上风电数据,结合LSTM或XGBoost等模型进行风电功率预测。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值