C语言基础 位域

C语言基础:位域

主题:位域(bit-field)
关键字:位域 冒号 结构体 存储空间
参考链接:C语言中文网:位域 、C菜鸟工具(在线编译器)、位域知乎问答
注:以下内容中如存在错误或缺漏,感谢评论指正,将尽快进行更正完善。


文章目录

  • C语言基础:位域
  • 一、位域是什么?
  • 二、为什么需要使用位域
    • 1. 减少数据存储空间
    • 2. 提高代码阅读性
  • 二、位域结构体成员的使用
    • 1.C 语言的位域结构体成员
    • 2.位域使用注意事项
    • 3.位域的存储
  • 总结



一、位域是什么?

struct
{
  unsigned int led_status     : 1; //led灯的开关状态,占1个bit
  unsigned int led_color      : 3; //led灯的颜色,占3个bit
  unsigned int led_luminance  : 4; //led灯的亮度等级,占4个bit
} status;

现实中的很多信息,例如设备的开关状态,并不需要占用一个完整的字节存储。为了更方便处理这类数据,C语言提供了一种数据结构类型,叫做“位域”,也叫“位段”。位域是一种结构体成员的类型,从字面意思理解,位域把一个存储单元,比如一个字节单元含8bit空间,按占用位bit大小划分出不同的区域,存放不同的数据。

bit76-43-0
位域成员存放数据1存放数据2存放数据3

二、为什么需要使用位域

1. 减少数据存储空间

为什么需要使用位存储?举个直观的例子:我们要控制2个彩色的灯,通过串口通信,给它下发控制指令,从而改变灯的开关状态和颜色状态。(表中data=0xda=0b11011010)
开关状态(占1位): 0=OFF ,1= ON
颜色状态(占3位): 0=红色,1=绿色,2=蓝色,3=黄色,4=紫色,5=白色。

字节存储方式(共4字节)byte1234
位域存储方式(共1字节)bit位76-432-0
数据定义灯a开关灯a颜色灯b开关灯b颜色
数据解释1=ON5=白色1=ON2=蓝色
data11011010

表中可以看出,通过位存储的方式,1个字节,就能实现2个灯的控制,而如果采用字节方式,控制2个灯需要4字节。假设要控制200个灯时,按位域方式进行的存储和通信,只需要100字节,而以字节方式则需要400字节。更少的通信数据,有利于保证通信效率,减少误码率,因此位域结构体在网络通信和CAN通信中较常见,有利于提高网络带宽利用率。
总结1:位域方式在数据存储中起到了减少存储空间的作用、在通信中起到减少通信数据量的作用。

2. 提高代码阅读性

接上面控制灯的例子,(表中data=0xda=0b11011010),现在要将灯a关闭,也就是第7bit置0,同时不能改变影响其他数据,也就是将数据变化为(data=0x5a=0b01011010)。虽然可以通过以下代码间接实现位域的赋值功能,但过程并不直观,代码理解起来费时。
总结2:使用位域结构体成员,有助于简化代码,使代码结构清晰

int main()
{
   #define LED_OFF  0
   unsigned char   led_data_init = 0b11011010;//灯总状态初始数据
   unsigned char   led_data = led_data_init;//灯总状态数据
	
   unsigned char   leda_status = LED_OFF;//灯a状态数据:关灯
   unsigned char   leda_status_bitsize = 1;  //灯a状态数据,占用led_data的1个bit
   unsigned char   leda_status_bitindex = 7; //灯a状态数据,存储在led_data的bit7位置(bit7-0)
	  	
   /**
   * 该宏函数得到一个将对应位域位置赋值0的值(bit7-0)
   * 例:0b0111 1111 = BIT_SEAT(1,7)
   * 例:0b0001 1111 = BIT_SEAT(3,5)
   */
   #define  BIT_SEAT(bit_size,bit_index)   (unsigned char)(~(~(0xff<< bit_size)<<bit_index))

   led_data &= BIT_SEAT(leda_status_bitsize,leda_status_bitindex);//led_data中对应域值清零
   led_data |= leda_status << 7;//led_data中对应域赋值新值
	
	//输出结果:led_data_init=0xda,led_data=0x5a	   
   printf("led_data_init=0x%x,led_data=0x%x",led_data_init,led_data);
   	
   return 0;
}

二、位域结构体成员的使用

1.C 语言的位域结构体成员

带有预定义宽度的变量被称为位域,位域在本质上就是一种结构类型,其成员按二进制位分配,它的基本用法与结构体一样。

struct 位域结构名 
{
	          
	类型说明符 type     位域名member_name :  位域长度 width ;
	类型说明符 type     位域名member_name :  位域长度 width ;
	......
	
}位域结构体定义的变量;

/**
* 以下代码定义了一个名为 struct bs 的结构体,
* data 为 bs 的结构体变量,共占四个字节:
*/
struct bs{
    int a:8; //变量a,占8bit
    int b:2; //变量b,占2bit
    int c:6; //变量c,占6bit
}data,*pdata;

pdata=&data;
data.b = 1;//位域的使用和结构成员的使用相同
pdata->c = 10;//位域结构体可以使用指针,位域成员不能使用指针。

2.位域使用注意事项

2.1.位域的类型
根据早期ANSI C规定,位域最开始只支持 int,unsigned int ,unsigned int三种类型,后面C99标准又增加了bool类型。但几乎所有的编译器都对此进行了进一步扩展,char ,unsigned char ,long,enum等都是可用的类型。实际应用中,尽量使用无符号类型,否则可能存在符号不一致问题。
位域不支持浮点型,否则编译提示:bit-field ‘c’ has invalid type。

2.2.位域的宽度
宽度必须小于或等于位域类型的位宽度,例如char类型,位宽度最大为8,int类型32位系统中,位宽度最大为32。错误的示例如下,编译器会提示错误:error: width of ‘a’ exceeds its type。

unsigned char  a:9; //错误的宽度定义,a最大为8。
unsigned int   b:33; //错误的宽度定义(假设在32位系统),32位系统中最大为32。

**注:**网上常见一种说法”位域不允许跨2字节,如一个字节所剩空间不够存放另一位域时,则会从下一单元起存放该位域,也就是说位域不能超过8bit。“这种说法并不正确,可能是英文翻译和理解错误。

比较恰当的说法是,**位域不允许跨2个类型单位,也就是说位域长度不能超过所依赖类型的最大位bit数。**例如,程序运行在32位系统,使用unsigned int,则位域宽度不能超过32。
使用以下例程序测试验证,刚好占32位,4个字节,位域数据紧密排列,并不存在一个字节所剩空间不够,从下一单元存放的现象。

struct bs{
    unsigned a:7;
    unsigned b:7;   
    unsigned c:7;    
    unsigned d:7;
    unsigned e:4;
    } n;
    //结果输出sizeof n = 4
   printf("sizeof n = %d\n",sizeof(n));

2.3.对位域结构体成员赋值不要超出其位域数值范围
在使用位域时,要特别注意检查数据赋值范围,虽然很基础但也最容易出错的操作,在进行位域成员赋值时,很可能不经意间就超出了范围。

struct
{
  unsigned int age : 3;//age 的赋值范围0-7
} Age;
 
int main( )
{
   Age.age = 8; // 二进制表示为 1000 有四位,超出
   printf( "Age.age : %d\n", Age.age ); 
   return 0;
}

位域成员赋值不要超过范围,以上代码编译时,会提示警告:第四位溢出,溢出的位会被忽略,只保留有效位,printf输出Age.age : 0

2.3无名位域、空域
位域可以是无名位域,也叫空域,这时它只用来作填充或调整位置。无名的位域是不能使用的。
无名位域的长度为0时,表示该单元后续空间都不能使用,需要从下一个新单元存储新数据。
已命名位域不能设置0长度(编译报错),位段定义的第一个位段长度不能为0(虽然编译不报错,但应避免这样使用)。

struct k{
    int a:1;
    int  :2;    /* 该 2 位不能使用 */
    int  :0;    /* 该int单元,后续位不能使用 */
    int b:2;    /* 存储在下一个新的int单元 */
};

2.4位域成员指针
C语言无法使用&对位域成员做取地址运算,不存在位域成员指针。


2.5位域数据的符号问题

在计算机中,所有的数据以补码的形式存储。使用有符号类型时,其最高位,会被作为符号位,数据以补码存储。
位域成员a,只有1位,0b1,最高位即本身,被当做符号位1。可以理解为向前补充符号位0xffffffff=-1,
位域成员b,有2位,0b11,最高位被当做符号位1,可以理解为向前补充符号位0xffffffff=-1。也可以通过补码转原码方式理解,补码的符号位1,数据为1,转换为原码,符号位1不变表示负号,数据为1,取反=0+1=1,整体表示-1。

正数补码等于原码,不存在符号变化,推荐位域中使用无符号类型。

{
struct bs{
    signed a:1;
    signed b:2;    
    } n ;
	
n.a=1;
n.b=3;

//打印输出a=-1,b=-1
printf ("a =%d, b=%d",n.a,n.b);
 

3.位域的存储

3.1位域数据的平台兼容性
位域天然的不利于兼容性。因为C语言标准并没有规定位域的具体存储方式,基于不同的编译器,不同的运行环境都可能有不同的实现。位域的数据存储,和编译器和运行环境有很大的关系,为了保证在不同平台运行结果的一致性,实现跨平台兼容。尽量使用无符号类型,并尽量简单的使用位域结构体成员。
如果位域在该项目中使用非常少,无关键用途,为了保证保证兼容性,可以选择不使用位域。

3.2类型相同的相邻位域数据存储

当相邻成员的类型相同时,位域按顺序优先存储在同一个单元中。
如果一个单元所剩空间不够存放另一位域时,则会从下一单元起存放该位域。如果剩余空间还够存放另一位域,那么后面的成员紧邻前一个成员存储,中间没有空隙。就像行李箱里放衣服一样,塞的下就继续放,塞不下就换下一个行李箱放。

struct bs{
 unsigned short a: 6;
 unsigned short b: 7;
 unsigned short c: 4;
 };

先定义的数据放在低位,单元1存完a、b后,剩余3bit空间放不下c,将c放到下一个单元中。
在这里插入图片描述
3.3不同类型的相邻位域数据存储问题
如果相邻位域字段的类型不同,则各编译器的具体实现不同。是否进行压缩,以及如何对齐都存在不确定性,建议编写例程,在具体环境下编译测试。

如果相邻位域字段的类型不相同,但数据长度相同,根据编译器不同,确定是否进行压缩,可能压缩。
如果相邻位域字段的类型不相同,但数据长度不同,根据编译器不同,确定是否进行压缩,通常不压缩。
如果相邻位域字段的类型不相同,总字节数为位域类型所占字节较大的整数倍,遵守结构体数据对齐规则。
如果位域中间穿插着非位域类型,则非位域数据,不参与压缩。可以简单理解为非位域类型,是独立的单元,本身已经占用了自身单元所有bit位置,没有空位给别的数据填充。

struct bs{
    unsigned a:7;
    unsigned b;//非位域数据,单独占一个unsigned int单元   
    unsigned c:1;    
    } n;

3.4位域数据的大小端存储及位序

数据的大小端及位序和编译器,处理器都有关系。
大小端是一种字节序,是指字节数据的外部排序方式,也就是多个btye数据在存储地址中的排序方式,单纯的大小端定义与字节内部存储无关。但通常,我们默认大端使用MSB,小端使用LSB,所以很多文章并不会特别指明位序,只提大小端,默认不提位序,导致对大小端的解释看起来有多个不同的版本。

模式应用
大端STM8,C51,TCP/IP协议
小端STM32,X86

ARM的大小端模式可配置,需要具体分析,最好运行测试代码进行验证确认,常见使用小端模式。


3.4.1大小端
大小端,是一种字节序,针对多字节数据。而单字节的数据例如char,不需要特意考虑大小端的问题。
首先明确区分两个高低,内存/存储地址高低,数据的高低字节

大端(Big-endian), 将数据的高位存储在内存的低地址处。
小端(Little-endian),将数据的低位存储在内存的低地址处。
“大小端”这个名词并不容易记忆和理解,它英文描述反而看起来更直观形象。例如,将“大端”翻译成“高尾端"更容易被理解,内存高地址存放数据的尾部低字节。大小指的是内存/存储地址,端指的是数据结束端,也就是数据的结束端字节(也就是最低字节)。

模式大小
大端(Big-endian)内存高地址数据的低字节
小端(Little-endian)内存低地址数据的低字节

例如数据 int a=0x12345678,在大小端系统中的存储。

内存地址大端小端
高地址00000004尾字节7812
000000035634
000000023456
低地址0000000112尾字节78

换一种更直接的理解和记忆方式,由于计算机总是从低地址往高地址方向读取,大端模式,先读到高字节,小端模式先读到低字节
基于这个特性使得大小端模式表现出各自的优缺点,符号位在高字节,大端模式能快速判断数据的正负和大小。小端模式,数据字节顺序和内存地址顺序一致,计算机按顺序读取更加高效。同时进行强制类型转换时,可以直接截取,不需要再调整内容。


3.4.2 位序
大小端,是指字节间的排序。字节内部的排序用位序表示。
位序,也叫比特序,分为LSB和MSB两种。
首先明确区分两个高低,内存/存储地址单字节空间内的bit高低位号,数据单字节内的bit权重高低位

LSB(least significant bit):数据的最低权重位存放在内存字节的第0位。
MSB (most significant bit):数据的最高权重位存放在内存字节的第0位。

小端模式下,只能使用LSB位序模式。
大端模式下,可以使用MSB位序模式,也可以使用LSB,大部分情况使用MSB。
在这里插入图片描述
无论LSB还是MSB,CPU都从内存中的低位开始读取,在MSB中读取到的0位内容为数据的高位,在LSB中读取到的0位内容为数据低位。虽然图中数据的存储在内存中不一样,但实际表示的都是同样的数据。对于一般的存储读取操作,字节序及位序并不影响数据读取结果。

但如果涉及大端系统与小端系统进行通信,或对数据进行分割截取,例如强制类型转换,就需要考虑字节序及位序影响。

在C结构体和位域中约定,无论LSB还是MSB,先定义的数据或位域总是放在低位地址。
可以这么理解,计算机总是从低位地址往高位地址寻址,总是先准备好低位的地址用来存储下一个数据,所以无论对结构体还是位域,先定义的数据先被分配,并优先分配到存储空间的低位地址的低位号。

先看LSB和MSB位序对单字节的影响。
在单字节下,小端模式和大端模式并不造成影响,但其默认采用的位序模式对数据造成了影响。

struct bs{
    unsigned  char a:1;
    unsigned  char b:2;
    unsigned  char c:5;    
    } ;
    
 union {
    struct bs n;
    unsigned char n_char;
   }u;
  
    u.n.a=1;
    u.n.b=1;
    u.n.c=1;
    printf("n_char=0x%.2x",u.n_char);

在LSB模式下,n_char= 0b00001011,0x0b
在MSB模式下,n_char=0b10100001,0xa1
在这里插入图片描述

在多字节下,也是一样的,先考虑位序的影响,同时还要附加考虑到大小端的影响。

struct bs{
    unsigned  short a:1;
    unsigned  short b:2;
    unsigned  short c:8;  
    unsigned  short d:2; 
    unsigned  short e:3; 
    } ;
    
 union {
    struct bs n;
    unsigned short n_short;
   }u;
  
    u.n.a=1;
    u.n.b=1;
    u.n.c=1;
    u.n.d=1;
    u.n.e=1;    
    printf("n_char=0x%.4x",u.n_short);

在小端模式LSB下,byte0 = 0x0b ,byte1 = 0b00101000 =0x28 ;小端模式下,byte1为高权重字节,n_short=0x280b;
在大端模式MSB下,byte0 = 0xa1,byte1 = 0b00101001 =0x29 ;大端模式下,byet1为低权重字节,n_short=0xa129;
在这里插入图片描述

可以看到同样的位域数据内容,存储在小端模式和大端模式下,进行整体取值后的值,变成了两个不同的值。所以在采用大端和小端的设备间进行相互通信,需要对数据进行转换后发送,或接收后重新解析。


3.4.3网络序
TCP/IP中,规定了自己的格式,与具体的CPU,操作系统等无关。从而保证了不同主机间通信格式统一。TCP/IP采用大端字节序,小端序主机进行大端网络序TCP/IP通信时候,需要使用htonl()、ntohl()等进行位序转换。
注:网络资料介绍:htonl()只做了字节序的转换,并没有进行比特序的转换。该知识涉及到更深入的网络通信底层,目前还不能理解。

总结

**位域的特点:**减少数据存储,提高代码阅读性,不利于平台兼容。

**位域的推荐使用方法:**简单的去使用位域,
位域推荐使用无符号类型,并尽量使用同一种类型,推荐使用unsigned int。将同类型的位域数据放在一起,尽量将位域数据成员相邻放置在一起,或单独打包成一个结构体。

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

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

相关文章

redis五种数据类型具体时候的底层编码

redis随着值的类型不同&#xff0c;其在底层编码类型会不相同。目前现有的编码格式有 #define OBJ_ENCODING_RAW 0 /* Raw representation */ #define OBJ_ENCODING_INT 1 /* Encoded as integer */ #define OBJ_ENCODING_HT 2 /* Encoded as hash table */ #def…

2023年打印机电商市场数据分析

近年来&#xff0c;伴随自动化办公及在线教育等场景的常态化&#xff0c;文件或学习资料等的打印需求不断增长&#xff0c;这也使得打印机需求暴增&#xff0c;打印机市场的市场规模也越来越大。 根据鲸参谋电商平台的相关数据显示&#xff0c;今年1月份至4月份&#xff0c;打印…

Unity UGUI1——基础组件概述

一、UGUI 介绍 ​ UGUI 是 Unity 引擎内自带的 UI 系统&#xff0c;官方称之为&#xff1a;Unity UI ​ 是目前 Unity 商业游戏开发中使用最广泛的 UI 系统开发解决方案 ​ 它是基于 Unity 游戏对象的 UI 系统&#xff0c;只能用来做游戏 UI 功能 ​ 不能用于开发 Unity 编…

简单的TCP网络程序·单进程(后端服务器)

目录 文件1&#xff1a;tcpServer.cc 文件2&#xff1a;tcpServer.hpp 1.提出日志概念 -- 在后续完善 日志格式 -- 暂定简单的打印功能 2.创建套接字 SOCK_STREAM -- socket参数 3.bind自己的套接字 4.设置socket 为监听状态 * 新接口1&#xff1a;listen 函数1&…

linux服务器上,docker安装nginx

如果出现 Using default tag: latest Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post "http://%2Fvar%2Frun%2Fdocker.sock/v1.24/images/create?fromImagenginx&taglatest": dial unix /va…

Python 中常用的数据类型及相关操作详解

文章目录 列表&#xff08;Lists&#xff09;创建列表访问列表元素添加元素到列表删除列表元素切片&#xff08;Slicing&#xff09;其他常用操作 元组&#xff08;Tuples&#xff09;创建元组访问元组元素元组长度其他常用操作 字符串&#xff08;Strings&#xff09;创建字符…

Three.js--》实现图片转3D效果展示

目录 项目搭建 初始化three.js基础代码 加载图片纹理 设置着色器 今天简单实现一个three.js的小Demo&#xff0c;加强自己对three知识的掌握与学习&#xff0c;只有在项目中才能灵活将所学知识运用起来&#xff0c;话不多说直接开始。 项目搭建 本案例还是借助框架书写th…

【Qt】Ubuntu安装GCC9.3.0版本的Qt5.15.5

目录 一、安装GCC9.3.0 1.下载GCC9.3.0源码 2.获取依赖项的包 3.生成Makefile文件 4.编译并安装 5.生成软链接 6. 查看GCC版本 二、安装Qt 1.下载Qt安装包 2.创建Qt项目并运行 一、安装GCC9.3.0 1.下载GCC9.3.0源码 https://ftp.gnu.org/gnu/gcc/gcc-9.3.0/gcc-9.3…

excel爬虫相关学习1:简单的excel爬虫

目录 1 什么是excel 爬虫 2 EXCEL爬虫 2.1 excel 爬虫的入口 2.2 需要配置的信息 2.2.1 如何获得 ua信息 2.3 获取的信息 2.3.1 获取信息的基本内容 2.3.2 获取过程 2.3.3 我们只用关注“表视图 ” 即可 2.4 EXCEL获得的爬虫数据 加载到excel里 2.5 数据到了excel表后…

Three.js--》实现3d汽车模型展览搭建

目录 项目搭建 初始化three.js基础代码 添加汽车模型展示 动态修改汽车模型 今天简单实现一个three.js的小Demo&#xff0c;加强自己对three知识的掌握与学习&#xff0c;只有在项目中才能灵活将所学知识运用起来&#xff0c;话不多说直接开始。 项目搭建 本案例还是借助…

flink主要组件及高可用配置

背景 flink不论运行在哪种环境&#xff0c;例如Yarn&#xff0c;Mesos&#xff0c;Kebernute以及独立集群&#xff0c;每个应用都会包含重要的几个组件&#xff0c;本文就来讲述下flink的主要组件以及如何实现flink的高可用配置 flink主要组件 如图所示&#xff0c;flink主要…

Java实训日志02

文章目录 八、项目开发实现步骤&#xff08;二&#xff09;创建项目1、创建Java项目2、创建目录&#xff0c;添加素材&#xff08;1&#xff09;创建help目录添加帮助文档&#xff08;2&#xff09;创建images目录添加图像素材&#xff08;3&#xff09;创建lib目录添加数据库驱…

Dockerfile创建镜像

一、Docker镜像的创建 创建镜像有三种方法&#xff0c;分别为【基于已有镜像创建】、【基于本地模板创建】以及【基于Dockerfile创建】。 1.1 基于现有镜像创建 &#xff08;1&#xff09;首先启动一个镜像&#xff0c;在容器里做修改docker run -it centos:7 /bin/bash …

设计模式之工厂方法模式笔记

设计模式之工厂方法模式笔记 说明Factory Method(工厂方法)目录UML抽象工厂示例类图咖啡抽象类美式咖啡类拿铁咖啡类 咖啡工厂接口美式咖啡工厂类拿铁咖啡工厂类 咖啡店类测试类 说明 记录下学习设计模式-工厂方法模式的写法。 Factory Method(工厂方法) 意图:定义一个用于创…

主从架构lua脚本-Redis(四)

上篇文章介绍了rdb、aof持久化。 持久化RDB/AOF-Redis&#xff08;三&#xff09;https://blog.csdn.net/ke1ying/article/details/131148269 redis数据备份策略 写job每小时copy一份到其他目录。目录里可以保留最近一个月数据。把目录日志保存到其他服务器&#xff0c;防止机…

专业的知识图谱应用门槛正在被不断降低

前⾔ 知识图谱&#xff08;knowledge graph&#xff09;⼀度被专家称为“AI皇冠上的明珠”&#xff0c;因为知识图谱技术是⼈⼯智能技术⽅向中的重要⼀环。它不仅可以为其他⼈⼯智能应⽤提供⽀持&#xff0c;如⾃然语⾔处理、推荐系统等&#xff0c;更可以帮助⼈⼯智能系统⾃主…

《微服务实战》 第三十一章 ShardingSphere - ShardingSphere-JDBC

前言 Apache ShardingSphere 是一款分布式的数据库生态系统&#xff0c; 可以将任意数据库转换为分布式数据库&#xff0c;并通过数据分片、弹性伸缩、加密等能力对原有数据库进行增强。 Apache ShardingSphere 设计哲学为 Database Plus&#xff0c;旨在构建异构数据库上层的…

【Python 随练】统计字符类型个数

题目&#xff1a; 输入一行字符&#xff0c;分别统计出其中英文字母、空格、数字和其它字符的个数。 简介&#xff1a; 在本篇博客中&#xff0c;我们将解决一个字符统计问题&#xff1a;输入一行字符&#xff0c;统计其中英文字母、空格、数字和其他字符的个数。我们将提供…

NoSQLBooster 8.0.11 for MongoDB

MongoDB最智能的IDE。 NoSQLBooster 是适用于 MongoDB Server 3.6-6.0 的跨平台 GUI 工具&#xff0c;它提供了内置的 MongoDB 脚本调试器、全面的服务器监控工具、链接流畅查询、SQL 查询、查询代码生成器、任务调度、ES2020 支持和高级 IntelliSense 体验。 嵌入式MongoDB S…

强化学习从基础到进阶-常见问题和面试必知必答[1]:强化学习概述、序列决策、动作空间定义、策略价值函数、探索与利用、Gym强化学习实验

【强化学习原理项目专栏】必看系列&#xff1a;单智能体、多智能体算法原理项目实战、相关技巧&#xff08;调参、画图等、趣味项目实现、学术应用项目实现 专栏详细介绍&#xff1a;【强化学习原理项目专栏】必看系列&#xff1a;单智能体、多智能体算法原理项目实战、相关技巧…