C语言指针小结2

文章介绍了C语言中的assert断言用于检测野指针,以及指针的传值调用、传址调用、数组指针、二级指针和函数指针的使用,包括qsort函数的应用,展示了指针作为复杂数据结构工具的强大之处。

接C语言指针小结1,本文讲讲指针的一些进阶用法,数组指针,函数指针等。

assert断言

在此之前先讨论一下assert断言,这是一种可以检查野指针的宏,需要使用前包含头文件assert.h

#include <assert.h>
int main()
{
    int a = 10;
    int* p = &a;
    assert(p != NULL);
    return 0;
}

上面代码运行时,验证变量p是否等于NULL 。如果确实不等于NULL ,程序会继续运⾏,否则就会终⽌运⾏,并且给出报错信息提示。而如果assert返回值为真时,不会有任何事发生。

而如果我们不需要assert断言时可以直接在程序前定义宏

#define NDEBUG

此时运行程序便不会进行assert断言。

指针使用

当我们使用指针时,因为是指向变量的地址,所以改变指针便改变了原来的变量

#include <assert.h>
int main()
{
    int a = 10;
    int* p = &a;
    *p = 20;
    return 0;
}

此时a变量便被改成了20,当然只是这样的话,无非是脱裤子放屁多此一举,不如直接改变a向量就好,学习到现在,我感觉指针变量更好的用处便是在函数里的应用。

传值调用

首先是函数的传值调用,比较基础的一个用法

#include <assert.h>

int add(int a, int b)
{
    return a + b;
}

int main()
{
    int a = 10;
    int b = 20;
    int c = add(a,b);
    return 0;
}

 add函数便是一个简单的加法函数,我们只需要把a和b的值传给他,然后他计算出a+b的值以后返回,我们用变量c储存结果。这个过程中我们并没有改变a和b变量的值,也不需要改变,所以直接进行传值调用。其实就是实参的值传给了形参,形参是实参的临时拷贝,计算完出函数销毁,传回计算结果。

传址调用

#include <assert.h>

void swap(int a, int b)
{
    int tmp = 0;
    tmp = a;
    a = b;
    b = tmp;
}

int main()
{
    int a = 10;
    int b = 20;
    swap(a,b);
    return 0;
}

此时运行程序得到的a和b是没有交换彼此的值的,为什么呢?很简单,我们只是把a和b实参的值传给了形参a和b,形参在swap函数中交换了值,但出了函数以后形参销毁——对形参的改变不影响实参。

#include <assert.h>

void swap(int* a2, int* b2)
{
    int tmp = 0;
    tmp = *a2;
    *a2 = *b2;
    *b2 = tmp;
}

int main()
{
    int a = 10;
    int b = 20;
    swap(&a,&b);
    return 0;
}

经此一改,我们进行传址调用,把a和b的地址传给指针a2和b2,改变指针便改变了指针指向的变量,swap函数得以实现。

数组指针 

数组名的含义 

 

#include <assert.h>



int main()
{
    int a[5] = { 1,2,3,4,5 };
    
    return 0;
}

此时数组a单独拿出来,当我们直接用数组名a时,他代表数组的第一个元素的地址,即&a[0].

而有两个例外:1.当数组名单独放在关键字sizeof()中时,此时我们的数组名代表整个数组的大小,此处便等价于5*4=20.

2.当数组名加取地址符号时,此时代表整个数组取地址,&a为整个数组的地址。欸,那和第一个元素的地址有什么区别吗,答案是没有,都是指向一个地址,都是首元素的地址,但是我们进行地址的加减时,步长便会发生变化,此时的+1和-1都是整个数组的步长,比如此时的int(*p)[5] = &a,我们的p+1指向的地址便是数组中最后一个元素后面的地址,因为直接一步走过了整个数组的步长。

其实我们在C语言中对数组进行定义时:int a[5] = { 0 },[]也是操作符,操作数为a和5,而我们访问数组中的元素时,比如a[2]即3这个元素时,就是先找到数组首元素地址,然后往后数三个数组中元素大小的步长,找到这个元素的地址,然后解引用得到这个数。

 

#include <assert.h>



int main()
{
    int a[5] = { 1,2,3,4,5 };
    a[3] == *(a + 3) == *(3 + a) == 3[a];
    return 0;
}

定义数组指针 

对于数组指针的定义与普通的指针变量有一点区别

 

#include <assert.h>

int main()
{
    int a[5] = { 1,2,3,4,5 };
    int (*p)[5] = &a;
    *(p + i) == a[i];
    return 0;
}

我们在定义时,首先看他是指针,所以在指针变量p前加*代表他是指针变量,然后就是后面的[5]代表他是数组指针,指向的数组有5个元素,最后时前面的int代表数组指针指向的数组中的元素时int类型。

相应的二维数组的定义

#include <assert.h>

int main()
{
    int a[3][2] = { 1,2,3,4,5,6 };//三行两列的二维数组
    int (*p)[2] = &a;
    *(p + i)[j] = a[i][j];
    return 0;
}

这里有一点,二维数组其实就是几个一维数组的集合,也就是二维数组可以看成是一维数组,这个一维数组的元素都是一维数组,所以这个数组名便代表了数组中第一个一维数组的地址,此时一个元素的大小时一个一维数组的大小,便可以通过指针来实现对二维数组中每一个数的访问,先通过数组名加减确定行数,然后通过[]访问每个数。

函数指针

首先函数的&取地址与否与加不加&无关,即&function == function。

而对于函数指针的定义也和数组类似,1.先是一个指针变量2.函数参数类型3.函数返回类型。

 

#include <assert.h>

int add(int a, int b)
{
    return a + b;
}

int main()
{
    int a = 10;
    int b = 20;
    int c = add(a,b);
    int (*p)(int, int) = &add;
    return 0;
}

指针数组 

顾名思义,存放指针的数组,定义的时候注意其实就是对数组的定义:

int * p[5] = { a, b, c, d, e };

就是一个放着五个指针的数组,元素的类型都是int*类型的指针变量。

函数指针数组

int (* a[2])(int, int) = {func1, func2}; 就是存放两个函数指针的数组,也是同样的道理。

只不过定义的时候我们把后面的a[2]放到了括号里,而数组里元素的类型是int(*)(int, int).

二级指针

简单来说就是指向指针的指针,有点套娃,但既是如此。因为指针变量他也是变量,变量的话就得在那内存申请空间存放他自己,那他就得有地址,我们便定义二级指针为指向这个指针变量的指针。

 

#include <assert.h>


int main()
{
    int a = 10;
    int b = 20;
    int *p = &a;
    int* *pp = &p;
    return 0;
}

其中第一个*和int一起代表我们的二级指针指向的变量是一个指针变量,类型是int*类型;而第二个*则表示这个变量pp 是一个指针变量。

qsort函数(数组排序函数)

首先给出qsort函数的定义:

void qsort( void* base, //指针,指向待排序数组的首元素
 size_t num,            //数组元素的个数
 size_t size,           //数组元素的大小(字节)
 int (*compar)(const void*, const void*)).  //比较函数,需满足当前面的变量大于后面的变量时返回大于0的数,等于返回0,小于返回小于零的数

这个函数是一个可以直接排序数组的函数,不论是数字数组还是字符数组(当然字符的话转化为ASCII码比较后排序),但使用时需要搭配比较函数compar来使用,当然这个函数需要我们自己写,通常数字的话可以直接return加减结果;而字符的话可以用strcmp函数比较(返回值正好满足compar要求,很完美)。

这里主要时函数指针的运用,还有回调函数,就是把函数指针作为参数传给另一个函数,当指针被用来调用其所指向函数的时候,被调用的那个函数我们叫做回调函数,在这里就是compar函数。

反思总结

比较笼统的介绍了一下指针,写到这有点晚所以最后的内容有点潦草,希望多多包涵。

日后有机会再精进提炼一下。

总的来说指针还是很单纯的,就是指向地址,但是衍生的东西较多,可能会产生混乱,但只需要按逻辑一步步来想,捋顺了逻辑,指针还是很好用的一个工具。

 

 

该数据集通过合成方式模拟了多种发动机在运行过程中的传感器监测数据,旨在构建一个用于机械系统故障检测的基准资源,特别适用于汽车领域的诊断分析。数据按固定时间间隔采集,涵盖了发动机性能指标、异常状态以及工作模式等多维度信息。 时间戳:数据类型为日期时间,记录了每个数据点的采集时刻。序列起始于2024年1224日10:00,并以5分钟为间隔持续生成,体现了对发动机运行状态的连续监测。 温度(摄氏度):以浮点数形式记录发动机的温度读数。其数值范围通常处于60至120摄氏度之间,反映了发动机在常规工况下的典型温度区间。 转速(转/分钟):以浮点数表示发动机曲轴的旋转速度。该参数在1000至4000转/分钟的范围内随机生成,符合多数发动机在正常运转时的转速特征。 燃油效率(公里/升):浮点型变量,用于衡量发动机的燃料利用效能,即每升燃料所能支持的行驶里程。其取值范围设定在15至30公里/升之间。 振动_X、振动_Y、振动_Z:这三个浮点数列分别记录了发动机在三维空间坐标系中各轴向的振动强度。测量值标准化至0到1的标度,较高的数值通常暗示存在异常振动,可能与潜在的机械故障相关。 扭矩(牛·米):以浮点数表征发动机输出的旋转力矩,数值区间为50至200牛·米,体现了发动机的负载能力。 功率输出(千瓦):浮点型变量,描述发动机单位时间内做功的速率,取值范围为20至100千瓦。 故障状态:整型分类变量,用于标识发动机的异常程度,共分为四个等级:0代表正常状态,1表示轻微故障,2对应中等故障,3指示严重故障。该列作为分类任务的目标变量,支持基于传感器数据预测故障等级。 运行模式:字符串类型变量,描述发动机当前的工作状态,主要包括:怠速(发动机运转但无负载)、巡航(发动机在常规负载下平稳运行)、重载(发动机承受高负荷或高压工况)。 数据集整体包含1000条记录,每条记录对应特定时刻的发动机性能快照。其中故障状态涵盖从正常到严重故障的四级分类,有助于训练模型实现故障预测与诊断。所有数据均为合成生成,旨在模拟真实的发动机性能变化与典型故障场景,所包含的温度、转速、燃油效率、振动、扭矩及功率输出等关键传感指标,均为影响发动机故障判定的重要因素。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值