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

C语言笔记(鹏哥)上课板书+课件汇总(结构体)-----数据结构常用

结构体

目录:

1、结构体类型声明
2、结构体变量的创建和初始化
3、结构体成员访问操作符
4、结构体内存对齐*****(重要指数五颗星)
5、结构体传参
6、结构体实现位段

一、结构体类型声明

其实在指针中我们已经讲解了一些结构体内容了,让我们回顾一下结构体是什么吧~
结构体:是一些值的集合,这些值称为成员变量,结构体的成员变量可以是不同类型的
1、数组与结构体:
数组:相同数据类型值的集合
结构体:不同数据类型的值的集合

struct tag
{member-list;//成员列表
}variable-list;//变量列表
  • 结构体可以更好的描述现实世界的复杂实体:现实世界中的实物都是具有属性和行为的,这里的属性就是不同数据类型的成员来描述的,也就是拿什么东西区描述它,而行为就是我们学习面向对象编程的类里的方法(没学过的可以暂时理解为函数),可以实现各种功能,也就是能拿他来做什么,对应不同的行为。

假设有一本书:它的属性是书名,作者,编号,价格,这些就是我们可以用结构体的成员列表来描述而他的行为,比如可以卖出去营利多少,这个就可以写个函数算一下。

//结构体类型:实体书具有的属性集合
struct Book
{   //成员列表char name[20];//书名char author[10];//作者char id[19];//编号float price;//价格
}b1,b2;//变量列表:这样创建是编译器依据数据类型结构体类型创建出来的全局变量
int main()
{
//直接在创建变量的同时对其进行初始化
//位置初始化:必须按照位置来初始化
struct Book b3 = {"鹏哥C语言","鹏哥","GF1234567",38.8f};
//关键字初始化:由你自己指定参数来初始化值,但是必须注意要访问到成员变量才可以进行赋值
struct Book b4 = {.author = "蛋哥",.id = "GF8765432",.name = "蛋哥linux",.price = 59.4f};
//打印结构体内容:通过.和->访问结构体成员
printf("%s %s %s %f\n", b3.author, b3.id, b3.name, b4.price);
p = &b4;
//结构体变量名.结构体成员名  ,   结构体指针->结构体成员名
printf("%s %s %s %f", p->author, p->id, p->name, p->price);
return 0;
}
  • 结构体类型其实就是一个数据类型的集合,把各种数据类型封装起来用于描述更为复杂的现实世界的实体
  • 那我们要创建出实体书才可以用结构体类型来描述他的属性呀,这个变量列表就是我们依据这些属性创建出的实体书,也可以在主函数内部创建并初始化实体书(结构体类型的实体)

打印出的结果:
在这里插入图片描述

  • 为什么这里的38.8并不是真正的38.8呢?

小数在内存(二进制)中是不一定能够精确存储的,
1、比如10.5 -->1010.1(后面的小数位正好是2**-1也就是0.5)刚好保存
10.8: 就是1010.1(是0.5),1010.11(是0.5+0.25),1010.111(是0.5+0.25+0.125)超纲了所以第三位小数要补0,那么接下来就又要用后面的数凑这个0.8,如果凑不齐就要已知凑下去
2、由于内存小数的精度是有限的,float小数最多32bit位,double最多是52个比特位,有没有可能有一个数字,你就一直往后面凑数就是凑不够,那是不是就是丢失精度了

  • 所以我们在比较浮点数的大小的时候实际上是需要接受他们之间有一定的误差值的,误差在多少范围内我认为他们相等,计算他们之间的浮点数差值绝对值,如果小于某个值就认为他们相等
int main()
{    if (fabs(38.8 - 38.8f) < 0.000001){printf("相等");}else{printf("不相等");}return 0;
}
int main()
{float f = 38.8;if (fabs(38.8 - f) < 0.000001){printf("相等");}else{printf("不相等");}return 0;
}

打印字符串的时候直接使用printf(“字符串内容”)就可以,因为实际上是将字符串的首地址传递给printf了,找到地址就可以直接将内容打印了

1、匿名结构体

1、在创建结构体的时候,可以不定义结构体的名字,但是这种结构体创建变量的时候只能够创建全局变量,如果没有对结构体重命名,基本只能使用一次
2、对于这样的结构体,即使内容一样,编译器一样会把他们当成不同的类型,因为没有名字

struct
{int a;char b;float c;
}x;
struct
{int a;char b;float c;
}a[20], *p;
*p = &x//不合法的:因为类型不一样

即使成员变量一样,但是没名字,编译器认为是两种不同的类型

二、结构体的自引用

结构体内包含有同类型的指针,记录着下一个结点的位置,以方便找到一下个数据

1、数据结构

数据结构其实就是数据在内存中的组织结构
线性数据结构

  • 1、顺序表:底层是数组,数据在内存中是连续存储的,只是在数组的基础上增加了一些功能,比如增删查改
  • 2、链表:数据在内存中不连续存储,一个节点中同时存放着数据域和指针域,一个结点有能力找到与它自身同类型的下一个结点,是通过下一个结构体的地址(指针域)来找到的
    在这里插入图片描述
  • 注意:这里有可能有的朋友们认为结构体的自引用难道不是直接在结构体的成员变量中引用自己同类型的下一个结构吗?
  • 那么请接收灵魂拷问:这种结构体的大小是多少?
  • 自己里面有同时包含数据域还有一个自己,这就很矛盾了呀
  • 那如果是结构体中同时包含数据域和指针域,这个结构体的大小就是可算的,数据域在内存中所占的字节数是可算的,指针的大小x86环境32bit是4字节,x64环境64bit是8字节

2、结构体的重命名

如果我们觉得结构体类型名字实在是太长了,想简写,就可以使用typedef来重命名,但是请注意顺序问题。

typedef struct Node
{int a;//数据域struct Node* next;//指针域
}Node;

将结构体类型由struct Node重定义为Node

1)如果这样使用结构体自引用看似合理却隐含顺序错误:

typedef struct Node
{int a;Node* next;
}Node;

我们是先声明了结构体struct Node,然后再去对其进行重命名的,而不是在创建的时候就可以直接用重命名后的结构体名称了
在这里插入图片描述

四、结构体的内存对齐(笔试常考题,重要程度五颗星)

引言:请朋友们看这里两段代码,试着运行一下:

#pragma pack(8)  //默认对齐数设置为8,编译器本身就是8,不知道为什么我的编译器没有内存对齐现象,大家可以试试如果输出结果是6 6 ,那么就需要加上这句话,如果是12 8,就不需要加上
struct S1
{char c1;int a;char c2;
}s1;
struct S2
{char c1;char c2;int a;
}s2;
int main()
{printf("%d\n", sizeof(s1));//printf("%d\n",sizeof(struct S1));printf("%d\n", sizeof(s2));//printf("%d\n",sizeof(struct S2));return 0;
}

在这里插入图片描述

  • 从运行结果可以看出,编译器并不是按照结构体成员变量所占内存的字节数来分配内存空间的,因为结果并不是6
  • 那么编译器到底是如何将结构体变量在内存中分配空间的呢?(其实就是给结构体类型分配空间,结构体内部是成员变量,也就是给结构体类型的各个成员变量分配空间)

1、内存对齐

这个规则就叫做结构体内存对齐:

  1. 结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处
  2. 其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。
    对⻬数 = 编译器默认的⼀个对⻬数 与 该成员变量⼤⼩的较⼩值。
  • VS 中默认的值为 8
  • Linux中 gcc 没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩
  1. 结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的整数倍。
  2. 如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。
  • 偏移量:从结构体变量在内存中开辟的空间的起始位置开始由0开始计数,第一个位置的偏移量是0,第二个位置偏移量为2…一直按照这个规则下去就形成了一段具标有偏移量的内存空间,如图所示:
    在这里插入图片描述
  • 而结构体第一个成员对齐到偏移量为0的位置,也就是把c1放置在标号为0的位置
  • 编译器默认对齐数:编译器自己决定的一个性质数,vs中默认是8,linux的gcc编译器就默认是成员本身,并不存在对齐数,所以就是按照字节数来分配空间,最终的对齐数,等于编译器默认对齐数和成员变量大小中的较小值,所以从第二个成员变量开始之后,每一个成员变量都是找对齐数,并且对齐到对齐数的整数倍的偏移量上,所以char c2就是对其到偏移量为1那,int n 自身大小是4,默认对齐数是8,最终对齐数是4,所以对齐到4的整数倍上就是偏移量是4那里开始
  • 最终将空间分配成这个样子:那么结构体的大小就是8字节啦占(8个内存空间,每个内存空间占1字节)
    *在这里插入图片描述
  • 这段代码的空间分配是这样的,这里up就不算啦,大家可以按照刚刚讲的方法试试呢~
    在这里插入图片描述

  • 第四条规则:关于嵌套结构体大小问题
  • 如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,这个外部的结构体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。
int main()
{struct S3{double d;char c;int i;};printf("%d\n", sizeof(struct S3));//练习4-结构体嵌套问题struct S4{char c1;struct S3 s3;double d;};printf("%d\n", sizeof(struct S4));return 0;
}
  • 这里的struct S3大小是16字节,可以自己练习练习奥~在这里插入图片描述
  • d占8字节,从偏移量为0开始对其占8字节,到编号为7的位置停止
  • c的大小1,默认对齐数8,最终对齐数是1,所以编号为8的偏移量是1的倍数,所以放在8位置
  • i的大小是4,默认对齐数8,最终对齐数是4,所以对齐到4的倍数处,9并不是4的倍数,所以不能再9的位置进行对齐,而12是4的倍数,所以从偏移量为12的位置开始对齐占4字节,所以到编号为15的位置,目前大小是16(从0开始占空间数)
  • 最终结构体的大小:所有的成员变量的最大对齐数是8,所以16是8的倍数,最终大小是16

  • c1是对齐到结构体起始位置偏移量为0的位置,
  • 结构体S3对齐到他的成员变量中最大对齐数那,最大对齐数是8,所以最终从偏移量为8的位置开始对齐,+16,对齐到偏移量为23的位置
  • d占8字节,对齐数8,所以从24开始对齐+8一直到编号为31的内存空间那,目前大小是32
  • 最终大小是所有成员变量最大对齐数的最大值的倍数,32是8的倍数

2、介绍一个宏:offsetof

可以计算结构体成员或者联合体相较于结构体变量起始位置或者是联合体变量起始位置的偏移量,返回的是一个unsigned int也就是一个无符号整形,使用%zd打印

  • offsetof(结构体类型,结构体成员变量)
    在这里插入图片描述
    在这里插入图片描述

和我们自己推测的在内存中的偏移量的起始位置是一致的:
在这里插入图片描述

在这里插入图片描述

五、为什么存在内存对齐呢?

⼤部分的参考资料都是这样说的:

  1. 平台原因 (移植原因):
    不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

2.性能原因:(后面有详细解释):

  • 数据结构(尤其是栈)应该尽可能地在⾃然边界上对⻬。
  • 原因在于,为了访问未对⻬的内存,处理器需要作两次内存访问;⽽对⻬的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,则地址必须是8的倍数。
  • 如果我们能保证将所有的double类型的数据的地址都对⻬成8的倍数,那么就可以⽤⼀个内存操作来读或者写值了。否则,我们可能需要执⾏两次内存访问,因为对象可能被分放在两个8字节内存块中。
  • 总体来说:结构体的内存对⻬是拿空间来换取时间的做法。
    在这里插入图片描述

解释:如果是一个32位机器,那么就有32根地址总线,每一根线都能读操作或写操作一个比特的地址,(读操作,写操作,以及数据是如何由内存传入cpu进行处理的,相关知识点请看深入指针1哈)
1、那么就是一次操作4字节,如果不进行内存对齐就会导致你需要两次访问才能把n这个整形类型的变量访问完全
2、如果对齐的,你只需要找到从4这个位置访问4字节,一次就能完全访问到n,但是相应的,它也会浪费一定的空间,相当于用空间换时间

1、如何更好的布局结构体呢?

那在设计结构体的时候,我们既要满⾜对⻬,⼜要节省空间,让占用空间小的成员尽量的集中在一起,这样可以很有效的利用本应该被浪费掉的空间。
在这里插入图片描述
在这里插入图片描述

  • 将c1和c2集中在一起,明显结构体占用空间小了
  • 注意:这里并不是说将小的成员变量都放在结构体的前边的意思,只是说把小的成员都集中在一起!,位置倒是无所谓,可以自己试试n,c1 , c2这个顺序,你会发现大小还是8

2、修改默认对齐数

#pragma 这个预处理指令,可以改变编译器的默认对⻬数。

#include <stdio.h>
#pragma pack(1)//设置默认对⻬数为1
struct S
{char c1;int i;char c2;
};
#pragma pack()//取消设置的对⻬数,还原为默认
int main()
{//输出的结果是什么?printf("%d\n", sizeof(struct S));return 0;

在这里插入图片描述
发现是6了,因为我们将对齐数修改为1了,就是紧挨着存放的意思,因为内存编号上的所有的值都是1的倍数嘛。

  • 在函数内部的执行顺序是这样的:首先主函数,找到结构体s,从前往后编译,默认对齐数被修改为1,计算出了大小是6,接着恢复了默认对齐数为8。
  • 但是请注意,一般我们的默认对齐数都设置成2的次方数,1,2,4,8,16…等等,而不会设置为3,5,7等等。

3、结构体传参

1、传值调用
2、传址调用

struct S {int a[10];float age;
};
void print1(struct S s1)
{int i = 0;for (i = 0; i < 10; i++){printf("%d ", s1.a[i]);}printf("\n");printf("%f", s1.age);
}
void print2(struct S* p)
{int i = 0;for (i = 0; i < 10; i++){printf("%d ", p->a[i]);}printf("\n");printf("%f", p->age);
}
int main()
{struct S s1 = { {1,2,3,4,5,6,7,8,9,10},20.0};print1(s1);//传值调用printf("\n");print2(&s1);//传址调用return 0;
}

在这里插入图片描述

  • 传值调用:直接传递结构体名字,形参同样使用结构体类型变量接收,形参是对实参的一份临时拷贝,会在内存中开辟一份临时的与实参等大的空间,在将实参中的数据拷贝出来进行函数处理,访问通过点操作符但是涉及到修改的地方是没办法在主函数中的原变量上体现的
  • 传址调用:&结构体名,传递结构地址,实参使用结构体类型指针接收,这个指针又有能力找到结构体,并且通过->能够访问结构体成员,并对其进行实质性的修改,可以实质性的对主函数中的相关变量修改
  • 所以其实传值调用能做的,传址调用都可以做做到但是传值调用未必能做传址调用能做到的(比如在函数内部修改主函数中的值)
  • ⾸选print2函数。
    原因:
    函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。
    如果传递⼀个结构体对象的时候,结构体过⼤,参数压栈的的系统开销⽐较⼤,所以会导致性能的下降。
    结论:
    结构体传参的时候,要传结构体的地址。

六、结构体实现位段

位段的声明和结构是类似的,有两个不同:

  1. 位段的成员必须是 int、unsigned int 或signed int或者char(本质上也是整形) ,在C99中位段成员的类型也可以选择其他类型。
  2. 位段的成员名后边有⼀个冒号和⼀个数字。位:指的是二进制位(bit),需要多大bit位空间就写多少
  3. 如果是char类型一次给1个字节操作,如果是int类型一次给4字节操作
struct S//结构体本来应该是16字节大小
{int _a;int _b;int _c;int _d;
}
struct A//实现位段之后就是8字节
{int _a:2;int _b:5;int _c:10;int _d:30;
};
int main()
{printf("%zd", sizeof(struct A));return 0;
}
  • 结构体中的每一个成员是int,本质上应该开辟4字节=32比特,但是这些bit位我并需要都用上,只需要用到一些,需要多少的比特位就在冒号后面写多少就可以了
  • 那么我们把位段中的位加在一起:47,那不应该是6字节嘛,哈哈别太过分啦,编译器能压缩到8字节已经很不错了,肯定会有一些空间浪费掉了,那么接下来跟我一起看看8字节是如何得来的吧~

1、结构体位段在内存中的分配

让我们看看这段代码:

struct A
{char _a : 3;char _b : 4;char _c : 5;char _d : 4;
};	
//A就是一个位段
int main()
{printf("%zd", sizeof(struct A));return 0;
}

在这里插入图片描述

  • 是这样的编译器一次性是可以给开辟好空间的,这里我为了方便就一个字节一个字节拿出来给大家演示

  • 在这里插入图片描述

  • 位段是有很多不确定性因素存在的:

  • 1、取出一个字节之后数据是由左向右还是由右向左,进行放入,是不确定的,取决于编译器,假设:从右向左

  • 2、如果一个字节剩余的比特位不够放下一个位段成员,是继续把剩余的比特位展占用,还是直接开辟一个新的字节空间进行使用是不确定的,假设浪费

  • 运行结果是3,果然vs编译器是这样默认的

那么对其进行赋值是不是我们所说的从右向左赋值呢,并且不够的位浪费掉呢,我们来验证一下

struct A
{char _a : 3;char _b : 4;char _c : 5;char _d : 4;
}a1;	
int main()
{a1._a = 10;a1._b = 12;a1._c = 3;a1._d = 4;return 0;
}

转换为二进制位是(因为内存空间是一字节也就是8bit,所以放入数据要转换为二进制放入,1个二进制位就是1个bit位):

  • 10:1010
  • 12:1100
  • 3:11
  • 4:100

推测一波是这样放的:

在这里插入图片描述

  • 由于a只开辟了3bit,所以存放的时候去掉最高位,注意这里都是这样的把超出的位高位去掉,留下符合数目的低位,那么这里留下剩余的三位进行存储,从右向左,剩余的空间补齐0,其余的以此类推
  • 最后由于在内存中存放的值是以十六进制查看的,所以我们将每4个bit位化为一组得到一个16进制数

那我们查看一下内存空间中的值:

2、如何打开内存

按住F11,点击调试–窗口–内存–窗口几都行,在输入&a1,就是找到a1在内存中的位置,好继续观察它在内存中存储的值
继续按F11,是进行一步一步调试用的,也就是程序一行代码一行代码执行,每次执行一行涉及到变量变化的,变量的值(监视窗口:打开方式一样只是打开的是监视)和内存中的值(内存窗口)都会有所变化。
在这里插入图片描述

  • 注意:这里采用了4列显示,大家右上角也有个列,可以自行调整,每一列都是1个字节(2个16进制的数,1个16进制数是4bit,2个16进制数正好是8bit,也就是1个字节),这里我们推测是用了3字节我就拿出了4个字节的空间查看效果,如果是整形的话4字节,大家也可以设为4列来查看整形的值,char的话1字节,大家可以1列查看,当然4列也是可以查看到效果的。
  • 大家在调试的过程中,执行完a1_a这句代码,会显示02 00 00 00,这是a的值被放入内存空间了,继续按住F11,执行完a1_b发现,62 00 00 00,这是b的值被放入了…,所以大家看到了确确实实是在一个字节中从右向左赋值的

3、位段成员没有地址

1、位段的⼏个成员共有同⼀个字节,这样有些成员的起始位置并不是某个字节的起始位置,那么这些位置处是没有地址的。内存中每个字节分配⼀个地址,⼀个字节内部的bit位是没有地址的。
2、所以不能对位段的成员使⽤&操作符,这样就不能使⽤scanf直接给位段的成员输⼊值,只能是先输⼊放在⼀个变量中,然后赋值给位段的成员。
在这里插入图片描述

struct A
{char _a : 3;char _b : 4;char _c : 5;char _d : 4;
}a1;	
//A就是一个位段
int main()
{int a = 10;scanf("%d",&a);a1._a = a;//正常scanf("%d", &a1._b);//报错return 0;
}

4、位段的不跨平台性

因为具有这些不跨平台性,所以要跨屏平台时,别使用位段
它既具有了在能实现结构体功能的基础上再减少空间,又带来了不跨平台的风险

  1. int 位段被当成有符号数还是⽆符号数是不确定的。

在位段中,如果位段成员是int类型就一次开辟4个字节进行操作,那么这四个字节的第一个bit位是0还是1是不确定的

  1. 位段中最⼤位的数⽬不能确定。(16位机器最⼤16,32位机器最⼤32,写成27,在16位机器会出问题。

在早期的16位机器中,int的大小是2字节,所以位段成员如果是int类型就必须不能超过16个bit,那么int _d:30;这个代码在16位机器上就是报错的,在32位机器上就是正常的

  1. 位段中的成员在内存中从左向右分配,还是从右向左分配,标准尚未定义。
  2. 当⼀个结构包含两个位段,第⼆个位段成员⽐较⼤,⽆法容纳于第⼀个位段剩余的位时,是舍弃剩余的位还是利⽤,这是不确定的。

5、位段的应用

下图是⽹络协议中,IP数据报的格式,我们可以看到其中很多的属性只需要⼏个bit位就能描述,这⾥使⽤位段,能够实现想要的效果,也节省了空间,这样⽹络传输的数据报⼤⼩也会较⼩⼀些,对⽹络的畅通是有帮助的。网路好比高速公路,如果都是一些小的数据,那么传输效率高,如果封装的很大,数据也多就会造成拥堵。
在这里插入图片描述

简易的讲解一下意思:计算机pc端1的用户发送了一条呵呵的短信,他怎么能够保证发送到你室友那,而不是发送给你的女朋友呢

计算机网络体系结构有许多层,每一层都有相应的功能,保证着你的数据正确发送,而每一层都有自己的数据格式,那么每一层就需要将要发送的数据进行加工(封装)上自己这一层的功能与格式之后进行相应处理,在继续传送数据,而到了网络层如果使用IP协议(协议就是使得计算机数据能够按照规定进行传输的约定),我们给它就数据封装上了数据上面的这些东西

类比一下你想发一个易碎的杯子快递,就需要层层给快递封装上泡沫,胶布,等等,还需要填写上相应的信息作为格式,如果你只是发一个很小的,那么我拿一个很大的箱子给你封装上就是浪费了,所以需要多少我就封装多大就可以了,那么位段同样也是这个道理,这回再去看开头的就比较好理解了

相关文章:

C语言笔记(鹏哥)上课板书+课件汇总(结构体)-----数据结构常用

结构体 目录&#xff1a; 1、结构体类型声明 2、结构体变量的创建和初始化 3、结构体成员访问操作符 4、结构体内存对齐*****&#xff08;重要指数五颗星&#xff09; 5、结构体传参 6、结构体实现位段 一、结构体类型声明 其实在指针中我们已经讲解了一些结构体内容了&…...

git清理--解决.git文件过大问题

背景&#xff1a;为什么.git比我仓库中的文件大很多 为什么我的git中只有一个1KB的README&#xff0c;但是.git却又1G多&#xff1f;当我想把这个git库push到gitee时&#xff0c;还会报错&#xff1a; 根据报错信息&#xff0c;可看出失败的原因是&#xff1a;有文件的大小超过…...

Jetson Orin NX 部署YOLOv12笔记

步骤一.创建虚拟环境 conda create -n yolov12 python3.8.20 注意&#xff1a;YOLOv12/YOLOv11/YOLOv10/YOLOv9/YOLOv8/YOLOv7a/YOLOv5 环境通用 步骤二.激活虚拟环境 conda activate yolov12 #激活环境 步骤三.查询Jetpack出厂版本 Jetson系列平台各型号支持的最高Jetp…...

微服务2--服务治理与服务调用

前言 &#xff1a;本文主要阐述微服务架构中的服务治理&#xff0c;以及Nacos环境搭建、服务注册、服务调用&#xff0c;负载均衡以及Feign实现服务调用。 服务治理 服务治理是微服务架构中最核心最基本的模块。用于实现各个微服务的自动化注册与发现。 服务注册&#xff1a;在…...

Arduino示例代码讲解:Project 08 - Digital Hourglass 数字沙漏

Arduino示例代码讲解:Project 08 - Digital Hourglass 数字沙漏 Project 08 - Digital Hourglass 数字沙漏程序功能概述功能:硬件要求:输出:代码结构全局变量`setup()` 函数`loop()` 函数计时和点亮LED:读取倾斜开关状态:重置LED和计时器:运行过程注意事项Project 08 - …...

python生成项目依赖文件requirements.txt

文章目录 通过pip freeze去生成通过pipreqs去生成 通过pip freeze去生成 pip freeze > requirements.txt会将整个python的Interceptor的环境下lib包下所有的依赖都生成到这个文件当中&#xff0c;取决于我们使用的python的版本下所有的安装包。不建议使用这种方式&#xff…...

C语言之高校学生信息快速查询系统的实现

&#x1f31f; 嗨&#xff0c;我是LucianaiB&#xff01; &#x1f30d; 总有人间一两风&#xff0c;填我十万八千梦。 &#x1f680; 路漫漫其修远兮&#xff0c;吾将上下而求索。 C语言之高校学生信息快速查询系统的实现 目录 任务陈述与分析 问题陈述问题分析 数据结构设…...

精益数据分析(7/126):打破创业幻想,拥抱数据驱动

精益数据分析&#xff08;7/126&#xff09;&#xff1a;打破创业幻想&#xff0c;拥抱数据驱动 在创业的道路上&#xff0c;我们都怀揣着梦想&#xff0c;但往往容易陷入自我编织的幻想中。我希望通过和大家一起学习《精益数据分析》&#xff0c;能帮助我们更清醒地认识创业过…...

Spring Boot 项目中发布流式接口支持实时数据向客户端推送

1、pom依赖添加 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId></dependency>2、事例代码 package com.pojo.prj.controller;import com.pojo.common.core.utils.String…...

Ubuntu安装MySQL步骤及注意事项

一、安装前准备 1. 系统更新&#xff1a;在安装 MySQL 之前&#xff0c;确保你的 Ubuntu 系统软件包是最新的&#xff0c;这能避免因软件包版本问题导致的安装错误&#xff0c;并获取最新的安全补丁。打开终端&#xff0c;执行以下两条命令&#xff1a; sudo apt update sudo …...

【网络篇】从零写UDP客户端/服务器:回显程序源码解析

大家好呀 我是浪前 今天讲解的是网络篇的第四章&#xff1a;从零写UDP客户端/服务器&#xff1a;回显程序源码解析 从零写UDP客户端/服务器&#xff1a;回显程序源码解析 UDP 协议特性​核心类介绍​ UDP的socket应该如何使用&#xff1a;1: DatagramSocket2: DatagramPacket回…...

MATLAB 控制系统设计与仿真 - 38

多变量系统控制器设计实例1 考虑如下给出的多变量系统模型&#xff1a; 考虑混合灵敏度问题&#xff0c;引入加权矩阵&#xff1a; 设计鲁棒控制器&#xff0c;并绘制闭环系统的阶跃响应曲线及开环系统的奇异值曲线。 MATLAB代码如下&#xff1a; clear all;clc; stf(s); g1…...

轻量化高精度的视频语义分割

Video semantic segmentation (VSS)视频语义分割 Compact Models(紧凑模型) 在深度学习中,相对于传统模型具有更小尺寸和更少参数数量的模型。这些模型的设计旨在在保持合理性能的同时,减少模型的计算和存储成本。 紧凑模型的设计可以涉及以下一些技术: 深度剪枝(Deep…...

Spring Boot 版本与对应 JDK 版本兼容性

Spring Boot 版本与对应 JDK 版本兼容性 以下是 Spring Boot 主要版本与所需 JDK 版本的对应关系&#xff0c;以及长期支持(LTS)信息&#xff1a; 最新版本对应关系 (截至2024年) Spring Boot 版本发布日期支持的 JDK 版本备注3.2.x (最新)2023-11JDK 17-21推荐使用 JDK 173…...

[密码学实战]详解gmssl库与第三方工具兼容性问题及解决方案

[密码学实战]详解gmssl库与第三方工具兼容性问题及解决方案 引言 国密算法&#xff08;SM2/SM3/SM4&#xff09;在金融、政务等领域广泛应用&#xff0c;但开发者在集成gmssl库实现SM2签名时&#xff0c;常遇到与第三方工具&#xff08;如OpenSSL、国密网关&#xff09;验证不…...

JavaScript模块化开发:CommonJS、AMD到ES模块

引言 在Web开发的早期阶段&#xff0c;JavaScript代码通常被编写在一个庞大的文件中或分散在多个脚本标签里&#xff0c;这种方式导致了全局变量污染、依赖关系难以管理、代码复用困难等问题。随着Web应用日益复杂&#xff0c;模块化编程成为了解决这些问题的关键。本文将带您…...

【k8s系列1】一主两从结构的环境准备

环境准备 虚拟机软件准备及安装&#xff0c;这里就不详细展开了&#xff0c;可以看文章:【一、虚拟机vmware安装】 linux环境准备及下载&#xff0c;下载镜像centOS7.9&#xff0c;以前也有写过这个步骤的文章&#xff0c;可以看&#xff1a;【二、安装centOS】 开始进入正题…...

dubbo SPI插件扩展点使用

参考&#xff1a;SPI插件扩展点 Dubbo SPI概述 使用IoC容器帮助管理组件的生命周期、依赖关系注入等是很多开发框架的常用设计&#xff0c;Dubbo中内置了一个轻量版本的IoC容器&#xff0c;用来管理框架内部的插件&#xff0c;实现包括插件实例化、生命周期、依赖关系自动注入…...

青少年编程与数学 02-016 Python数据结构与算法 26课题、生物信息学算法

青少年编程与数学 02-016 Python数据结构与算法 26课题、生物信息学算法 一、序列比对算法二、基因表达分析算法三、蛋白质结构预测算法四、系统生物学模型构建算法五、单细胞分析算法六、遗传关联分析算法七、机器学习与数据挖掘算法八、数据可视化算法九、代谢组学分析算法十…...

【Rust 精进之路之第2篇-初体验】安装、配置与 Hello Cargo:踏出 Rust 开发第一步

系列&#xff1a; Rust 精进之路&#xff1a;构建可靠、高效软件的底层逻辑 **作者&#xff1a;**码觉客 发布日期&#xff1a; 2025-04-20 引言&#xff1a;磨刀不误砍柴工&#xff0c;装备先行&#xff01; 在上一篇文章中&#xff0c;我们一起探索了 Rust 诞生的缘由&…...

洛谷题目:P7775 [COCI 2009/2010 #2] VUK 题解 (本题简)

题目传送门: P7775 [COCI 2009/2010 #2] VUK - 洛谷 (luogu.com.cn) 前言: 这道题的核心目标是找出狼从起点 V 到终点 J 的路径,使得狼在途中离它最近的树的距离的最小值最大。下面为大家详细讲解: #整体思路概述: 这道题我们可以采用“先计算距离,再来二分查找”的…...

腾讯旗下InstantCharacter框架正式开源 可高度个性化任何角色

目前基于学习的主题定制方法主要依赖于 U-Net 架构&#xff0c;但其泛化能力有限&#xff0c;图像质量也大打折扣。同时&#xff0c;基于优化的方法需要针对特定主题进行微调&#xff0c;这不可避免地会降低文本的可控性。为了应对这些挑战&#xff0c;我们提出了 “即时角色”…...

Python爬虫实战:获取fenbi网最新备考资讯

一、引言 1.1 研究背景 伴随互联网技术的迅猛发展,在线教育平台积累了海量备考数据。以粉某网为例,其备考数据涵盖考试资讯、备考资料、用户评价等,对备考者意义重大。然而,获取并分析这些数据颇具挑战,需借助先进的爬虫技术和数据分析方法。 1.2 研究目的 本研究旨在…...

详讲Linux下进程等待

3.进程等待 引言&#xff1a;什么是进程等待 想象有两个小伙伴&#xff0c;一个是 “大强”&#xff08;父进程 &#xff09;&#xff0c;一个是 “小强”&#xff08;子进程 &#xff09;。大强给小强安排了任务&#xff0c;比如去收集一些石头。 …...

JBoss + WildFly 本地开发环境完全指南

JBoss WildFly 本地开发环境完全指南 本篇笔记主要实现在本地通过 docker 创建 JBoss 和 WildFly 服务器这一功能&#xff0c;基于红帽的禁制 EAP 版本的重新分发&#xff0c;所以我这里没办法放 JBoss EAP 的 zip 文件。WildFly 是免费开源的版本&#xff0c;可以在红帽官网找…...

【网络原理】TCP协议如何实现可靠传输(确认应答和超时重传机制)

目录 一. TCP协议 二. 确定应答 三. 超时重传 一. TCP协议 1&#xff09;端口号 源端口号&#xff1a;发送方端口号目的端口号&#xff1a;接收方端口号 16位&#xff08;2字节&#xff09;端口号&#xff0c;可以表示的范围&#xff08;0~65535&#xff09; 源端口和目的…...

【国家能源集团生态协作平台-注册/登录安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…...

Java表达式1.0

Java开发工具 在当今的Java开发领域&#xff0c;IntelliJ IDEA已然成为了众多开发者心目中的首选利器&#xff0c;它被广泛认为是目前Java开发效率最快的IDE工具。这款备受瞩目的开发工具是由JetBrains公司精心打造的&#xff0c;而JetBrains公司总部位于风景如画的捷克共和国首…...

idea中导入从GitHub上克隆下来的springboot项目解决找不到主类的问题

第一步&#xff1a;删除目录下的.idea和target&#xff0c;然后用idea打开 第二步&#xff1a;如果有需要&#xff0c;idea更换jdk版本 原文链接&#xff1a;https://blog.csdn.net/m0_74036731/article/details/146779040 解决方法&#xff08;idea中解决&#xff09;&#…...

Android音视频开发

Android Framework 与音视频技术深度解析 一、Android音视频架构全景 ▶ 四层架构协同┌──────────────┐│ 应用层 │ ▶ MediaPlayer/ExoPlayer/Camera2 API调用└──────┬───────┘┌──────▼───────┐│ 框架层 │…...