结构体的理解

news/2024/5/1 9:30:15/文章来源:https://blog.csdn.net/qq_62106937/article/details/126904144

结构体

  • 前言
  • 结构体?
    • 定义变量
    • 如何赋初值?
    • 结构体的访问
    • 结构体的嵌套使用
    • 注意事项
  • 结构体的大小
    • 内存对齐
    • 默认对齐数的修改
    • 为什么存在内存对齐?
    • 结构体传参
  • 位段
    • 什么是位段?
    • 位段的内存分配
    • 深入剖析位段“存”数据
    • 位段的“取”
    • 位段的跨平台问题
    • 位段的应用

前言

记录对于结构体知识的了解

结构体?

结构体是一种复杂类型对象,可以用来描述一些复杂对象;
比如说:对于一个人来说,他不光只有年龄、身高、体重、姓名等,我们通过基本数据类型无法来描述它,为此我们得想办法,解决他啊!C语言为我们提供了struct关键字来解决这个问题,利用struct关键字我们可以用来定义结构体,把这些原本单个的数据给它打包在一起,形成一个新的类型;
那么struct怎么用呢?
举个例子,比如说我们要描述人:

struct Person
{
char name[20];
char sex[5];
int age;
}

我们想这样定义就好了,其中struct Person就是和int 、char、double同等地位的东西了,struct Person也是一个数据类型,只不过它是复杂数据类型,由基本数据类型组成,能描述的信息也就更多。这也是C语言的灵魂之一;
基本语法就是:

struct +类型名
{
成员变量;
};//主义这里要加分号结尾;
因此我们有了这样的一个类型,我们该如何定义变量呢?

定义变量

上面我们说了struct定义出来的复杂数据类型和基本数据类型是一样的地位,都是类型,那么自然的定义变量的方式也是一样的;
基本数据类型是怎么定义的我们就怎么定义:
在这里插入图片描述
红色部分是我们的类型,黑色部分是变量;

如何赋初值?

赋初值就和基本数据类型有所区别了,基本数据类型,只有一个,我们只需要=进行赋初值就行了;但是复杂数据类型不一样啊,它里面有多个基本数据类型,为了能给每个都赋值到位,我们应该用{ }的形式进行赋值操作,其中格式按照基本数据类型对应的格式就好了:
比如:

struct Person p1={"张三","man",19};

必须按照基本数据类型的顺序正确赋值,不能随意颠倒顺序;!!!
就像数组那样一样赋值就像了;
我们还有一直赋初值的方式:
上面一种方式不是要求我们不能颠倒赋值顺序嘛,这一种方式就是破解上一个方式的:

struct Person p1={.age=19,.sex="man",.name="张三"};

像这样的赋值方式相当于我们“指哪打哪”想给那个成员变量赋值,就给那个成员变量赋值;这里我们讲到了**.**(点)这个操作符,也就是访问结构体的方式;

结构体的访问

既然我们定义好了结构体同时也对结构体进行了相应的初始化,那么我们应该如何去访问结构体里面的数据呢?
C语言为我们提供了两个操作数去解决这个问题:
在这里插入图片描述
.操作符和->操作符;
.操作符是配合着普通结构体变量来使用的;
->操作符是配合结构体指针来使用的;
具体怎么使用呢,我们来具体看一看:
1、.操作符使用
在这里插入图片描述
2、->操作符使用
在这里插入图片描述

结构体的嵌套使用

当然我们的结构体不是只能由基本数据类型组成,还可以由结构体组成:
比如说:
在这里插入图片描述

注意事项

**1、**结构体并不一定要有名字,匿名结构体(没有名字的结构体也是可以的),但是该结构只能用一次;什么意思呢?
我们来看代码;
在这里插入图片描述
我们不能单独将匿名结构体拿出来创建变量,我们只能在定义结构体的的时候随便创建一个变量(类似于左边图),这就是为什么匿名结构体只能使用一次?
那么如果现在有我定义了两个一模一样的匿名结构体,编译器会认为这两个结构体是同一个数据类型吗?
我们来验证一下:
测试代码:

struct 
{char name[20];char sex[5];char id[20];int age;
}str1 = { "张三","man","110112119112",10 };
struct
{char name[20];char sex[5];char id[20];int age;
}*str2;int main()
{str2 = &str1;return 0;
}

在这里插入图片描述
我们可以看到虽然编译器让段代码通过了,但是还是保了个警告;
我们知道“=”两边的数据类型必须一样,现在报了警告,这说明现在“=”两边的数据类型出现了不统一,这也就说明了虽然两个匿名结构体虽然成员变量完全一模一样,但是在编译器看来他们仍然是两个不同的数据类型;当然对于两个完全相同的有名结构来说就不会存在这样的情况,编译器会直接给你报错误,说你重定义了这个数据类型:
在这里插入图片描述
**2、**对于结构体来说,它的类型名字一般都比较长,我们有没有什么办法使它的名字变得短一点;当然可以,我们可以利用typedef关键字对类型进行重名名,比如说:
在这里插入图片描述
我们利用了typedef使得原来冗长的数据类型变得简单起来了,虽然简单起来了,我们确不能滥用,因为我们利用typedef过后,代码可读性变差了,就比如:Person,单单放在这里,我们可以说他联合体类型、也可以说他是枚举类型同时也可以说他是结构体类型,给人的可读性不是太友好;当然这只是一方面的原因,同时一些公式也会对此做一些代码规范的要求,有些公式可能为了代码的可读性,禁止使用typedef,当然有的公司则无此限制;具体怎么使用看个人情况;
**3、**我们定义一个结构体最好定义在函数的外面,不要再函数内部定义,这样的话我们才能进行更多的操作和减少不必要的麻烦;
**4、**结构体虽然能嵌套使用,但是不能嵌套自己;
比如这样:
在这里插入图片描述
为什么呢?
你看我们好像无法计算出struct Person这个结构体的大小对吧,我们知道name是20个字节,那struct Person是多大呢?你看我们是不是又回到了原点,似乎该计算过程在无限递归下去,我们无法求的一个准确的大小,编译器也就无法为其开辟一个合理的空间;
但是呢我们可以嵌套自己类型的指针啊,你想啊指针的大小是固定的(4/8)我们可以明确算出结构体的大小啊!
在这里插入图片描述
事实的确是这样的!

结构体的大小

既然上面我们谈到了结构体的大小问题,那我们便来看看一个结构体,
并算一算结构体的大小是多少;

struct Person
{char a;char b;int c;
};

我们来算一算这个结构体的大小
可能我们第一次算的时候会算成6;那么结果是不是呢,我们运行一下:
在这里插入图片描述
我们可以看到运行结果是8,这是为什么??
这主要是由于啊,结构体的大小并不是简单的相加就解决的,对于结构体来说,它们存在这内存对齐这一说法;
何为内存对齐?

内存对齐

内存对齐:

  1. 第一个成员在与结构体变量偏移量为0的地址处。
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
    对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
    VS中默认的值为8
  3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

下面我们就来具体应用一下该规则(VS2019环境,默认对齐数:8):
在这里插入图片描述
下面我们来放空间:
在这里插入图片描述
这也就是为什么该结构体大小是8的原因;
既然明白了原理,我们立刻趁热打铁,继续算几个:

struct S2
{
char c1;
int  c2
char c3;
};
struct S3
{
double d;
char c;
int i;
};

我们画一个s3吧,另一个当作练习:
在这里插入图片描述
空间分布:
在这里插入图片描述
运行结果:
在这里插入图片描述
的确如此;
那么我们现在来看一个结构体嵌套的结构体的大小:

struct S3
{
double d;
char c;
int i;
};
struct S4
{
char a;
int b;
struct S3 e;
};

由上面可知S3的总大小是16个字节,然后呢根据内存对齐的第4条规则,我们可以得出S3结构体的对齐数为其成员对齐数的最大值,也就是:8,因此S3的对齐数为8;
接下来我们算一下对齐数表:
在这里插入图片描述
接下来分配内存:
在这里插入图片描述
运行一下:
在这里插入图片描述
的确如此!!!

默认对齐数的修改

既然由默认对齐数这个东西,我们能不能去修改掉它呢?
当然可以;

我们可以通过预处理指令来修改
比如#pragma pack(4)//修改默认对齐数为4
#pragma pack()//取消默认对齐数修改

下面我们还是来计算

struct S3
{
double d;
char c;
int i;
};

我们快速计算一下:
0~7//d占用
8//c占用
9~11//浪费
12~15//i占用
总的:16字节,最大对齐数是4,16是4的整数倍,故最后结构体总大小为:16字节;
运行截图:
在这里插入图片描述
同时我们可以利用offsetof宏函数来计算一下每个成员变量的偏移量,同时验证我们的推理过程是否正确;
0~7//d从偏移量为0占用
8//c从偏移量为8占用
9~11//浪费
12~15//i从偏移量为12占用
在使用前先了解一下offsetof宏函数:
在这里插入图片描述

包含在同文件<stddef.h>里面;
运行结果:
在这里插入图片描述
的确如此;

为什么存在内存对齐?

从两个方面来说:

1、平台原因(移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2、 性能原因:
数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
比如说:32位平台,一次性只能读取四个字节的数据;

struct S
{
char a;
int i;
};

在这里插入图片描述

那在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到:
让占用空间小的成员尽量集中在一起。

结构体传参

struct S
{
int data[1000];
int num;
};
struct S s = {{1,2,3,4}, 1000};
//结构体传参
void print1(struct S s)
{
printf("%d\n", s.num);
}
//结构体地址传参
void print2(struct S* ps)
{
printf("%d\n", ps->num);
}
int main()
{
print1(s);  //传结构体
print2(&s); //传地址
return 0;
}

上面的 print1 和 print2 函数哪个好些?

答案是:首选print2函数。
原因:
函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。

结论:
结构体传参的时候,首选传结构体的地址。

位段

结构体的基础知识讲完了,我们就来谈谈结构的位段;

什么是位段?

1、位段与结构体的声明是类似的,但是位段的成员必须是int、unsigned int、signedint、char;
2、位段的成员名后面有一个“ : ”和一个数字,以" ; "结尾;
3、访问的方式与结构体一样(可以通过点操作符和箭头操作符);
4、位段不存在内存对齐;

具体实例:

struct A
{
int _a:2;
int _b:5;
int _c:10;
int _d:30;
};

首先我们来解析一下,这个位段的位是什么意思,以及" : "后面的数字代表什么意思?
首先,位嘛,就是比特位的那个位,而冒号后面的数字,就表示这个变量要占用几个比特位;比如上面结构体中,int _a:2;就表示成员变量_a只需要使用2个比特位就够了,不在需要一个int的大小;

位段的内存分配

  1. 位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型
  2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。
  3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。
    4、不建议int和char混用

在讨论位段的内存分配之前,我们先来算一个位段的大小:

struct S
{
char a:3;
char b:4;
char c:5;
char d:4;
};

在这里插入图片描述
我们可以看到该结构只占3个字节,其实按照我们之前的理论我们可以得出相应的结论;
是char类型的位段;
编译器一上来先开辟1个字节,给你用,用完了无,我在给你开辟;
然后a说我要3个比特位,还剩下5个比特位;
b说我要4个比特位,还剩下1个比特位;
c说我要5个比特位,这时候只剩下一个1特位,不够啊,那我再给你开辟一个字节;嗯,现在空间够了,但是我们现在面临一个选择的问题,就是上一次剩下的那个字节,我到底是用还是不用?这个问题C语言并没有做出明确规定,是由编译器决定的,这是位段可移植性差的一个点,再VS2019中,编译器是不用的(博主用的VS2019),那我们现在假设不用那么我在新开的一个字节中用了5个比特位,还剩下3比特位;
d说,我要4比特位,不够啊,编译器在开一个字节,这时就够了,总体来说,编译器一共就开辟了3个字节,因此,我们sizeof算出来的就是3字节;

深入剖析位段“存”数据

我们刚才讨论了位段的空间大小,现在让我们更进一步去研究一下,位段是怎么存数据的;

还是以上述位段为例:
测试代码:

struct S
{
char a:3;
char b:4;
char c:5;
char d:4;
};
struct S s = {0};
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;

首先,我们直到S会占3字节,我们把这3个字节全部用0初始化:
则这3个字节的每个位里面放的都是0;
现在,我们将10的放进去:
10
补码:00000000000000000000000000001010
我们只存3位进去,也就是只有后3位,010,
在这里插入图片描述
但是现在我们面临一个问题选择问题?
我们是从左边还是右边来使用这一个字节?这是不确定的,C语言没有明确规定,完全取决于编译器,因此这是位段可移植性差的一个点;
我们假设是从右边开始存的,那么该字节内容就变为了:
在这里插入图片描述
然后我们再来看b,b=12
12:
补码:
00000000000000000000000000001100,我们只存4位进去,也就是(1100);更新一下第一字节里面的内容;
在这里插入图片描述
现在s.c=3;
3的补码:00000000000000000000000000000011
我们要存5个比特位进去(00011),现在不够了,我们在开辟一个字节,
在这里插入图片描述
但是现在我们又面临一个选择的问题,我们用绿色线圈起来的那个位,我们到底用不用?还是直接从新的字节开始?对于这样的问题C语言没有做出明确规定,用还是不用这个问题却决于编译器,这也是位段可移植性差的又一个原因;现在我们接着假设,这个比特位咱们不用,那么我们新的内容就是:
在这里插入图片描述
接着s.d=4;
4的补码:00000000000000000000000000000100
我们只存4比特位进去(0100),不够啊,我们再开一个字节:
在这里插入图片描述
在这里插入图片描述
我们来算一下这3个字节对应的16进制:62 03 04
我们看到的就应该是这样的,我们来验证一下:

在这里插入图片描述
我们可以看到的确是这样,那说明我们之前的假设刚好假设正确;事实上这是VS2019对于位段的处理方法;

位段的“取”

既然我们利用位段存了数据,我们得取出来啊?不然我们存进去了,取不出来,有啥用啊;
根据我们刚才存进去的样子:
在这里插入图片描述
我们以%d打印s.a那么a实际存的是010,最高位为0,我们会发生整型提升,提升成int类型:
既printf实际打印的是
00000000000000000000000000000010
原码就是2;
在这里插入图片描述
我们再来看b,b里面实际存的是1100,%d打印发生整型提升:
11111111111111111111111111111100%d打印,符号位为1,为负数
1000000000000000000000000011
1000000000000000000000000100
也就是-4;
在这里插入图片描述

如果是%u打印,就是直接打印:11111111111111111111111111111100
也就是一个很大的数:

在这里插入图片描述

位段的跨平台问题

  1. int 位段被当成有符号数还是无符号数是不确定的。
    (对于一个int位段的的位段,我们一次性会开辟4字节,但是这4字节的最高位会不会北当成符号位,是不确定的,是由编译器决定的)
  2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机
    器会出问题。
    比如:
struct S
{
int a:2;
int b:20;
int C:30;
}

在32位平台下,int占4个字节,但是在早期16位平台下int占2个字节,我们如果要给b分给20比特位,在32位平台下没问题,但是在16位平台下就跑不过去了;
3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是
舍弃剩余的位还是利用,这是不确定的。

总结:跟结构相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在。

位段的应用

在这里插入图片描述
位段的最大优点就在于可以最大限度的节省空间,就比如,对于一些数据来说,我们只需要4比特位来存储就够了,就没必要开辟一个int来存,这样大大节省了空间!!!

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

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

相关文章

Idea工具中,使用Mapper对象有红线

背景&#xff1a; IDEA开发工具&#xff0c;springboot mybatis项目 &#xff08;这个是不需要改的&#xff0c;也不算是问题&#xff0c;因为项目并不会报错&#xff0c;只是作者好奇找了下问题&#xff0c;并记录一下&#xff09; 问题描述 mapper对象在service层有红线&a…

8 位卷王!总结 1135 页 Java 核心面试手册,硬钢 BATJ 一线大厂面试官

又到了金九银十求职季&#xff01; HR 开始拼业绩&#xff0c;招聘网站也开始释放出大量岗位&#xff0c;转行跳槽、毕业求职的人都开始行动起来&#xff01; 此时&#xff0c;对于大多数程序员来说&#xff0c;最大的目标就是&#xff1a;进大厂&#xff01; 大厂为什么这么…

ArcGIS Map Sdk for unity使用

本文主要讨论离线模式。 目录 1.底图tpk文件制作 2.3D图层slpk文件制作 3.导入使用 1.底图tpk文件制作 软件&#xff1a;91卫图助手 Arcgis Pro 操作步骤&#xff1a; 打开91卫图助手&#xff0c;更换底图为高德影像/腾讯影像。(百度影像的地理投影格式有自身加密&#xff…

剖析容器运行时

特别说明&#xff1a;一部分转载自大佬文章&#xff1a;https://blog.csdn.net/weixin_39246554/article/details/120926174&#xff08;不得不说大佬总结的真好啊&#xff01;&#xff01;&#xff01;&#xff09; 剩下的听老王公开课总结。 k8s官网关于运行时的说明&#x…

Typora Mac版本安装Pandoc导出文件为word格式(windows可通用)

我们在用Typora时导出的格式常常为PDF格式&#xff0c;但是如果我们要将文件导出为word格式的时候却需要安装插件PanDoc&#xff0c;我目前使用的是Mac版本的Typora&#xff0c;给大家分享一下如何安装Pandoc以及导出word格式文件。 1.根据Typora中的说明进入GitHub下载Pandoc…

Maven安装配置

Maven安装配置一、下载 apache-maven-3.6.1Maven官网:https://maven.apache.org/download.cgi(或)直接下载maven-3.8.6:https://dlcdn.apache.org/maven/maven-3/3.8.6/binaries/apache-maven-3.8.6-bin.zip解压到当前文件夹二、配置 maven 环境变量右键此电脑 - 属性 - 高级…

MySQL学习——执行计划

MySQL中可以通过explain关键字模拟优化器执行SQL语句,从而知道MySQL是如何处理SQL语句的,这将有利我们做代码的优化。 1、MySQL查询执行过程客户端向MySQL服务器发送一条查询请求 服务器首先检查查询缓存,若缓存中存在,则立刻返回存储在缓存中的结果。否则进入下一阶段 服务…

扫码挪车小程序源码专业版上线了

1 、做挪车码之前&#xff0c;先说一些我个人的观点&#xff0c;大家一起探讨学习交流。 2 、挪车码已经是普遍已久的项目&#xff0c;其核心主要在于解决了车主的隐私问题。 3 、观察过目前市面上所有的挪车码系统&#xff0c; 公司也购买了一套测试了完整流程&#xff0c;盈…

【图像分割】基于matlab萤火虫算法图像分割【含Matlab源码 2136期】

一、获取代码方式 获取代码方式1: 完整代码已上传我的资源:【图像分割】基于matlab萤火虫算法图像分割【含Matlab源码 2136期】 点击上面蓝色字体,直接付费下载,即可。 获取代码方式2: 付费专栏图像处理(Matlab) 备注: 点击上面蓝色字体付费专栏图像处理(Matlab),…

parted分区步骤

parted分区步骤概述 通常我们用的比较多的一般都是fdisk工具来进行分区,但是现在由于磁盘越来越廉价,而且磁盘空间越来越大;而fdisk工具他对分区是有大小限制的,它只能划分小于2T的磁盘。但是现在的磁盘空间很多都已经是远远大于2T了,甚至达到2.5T和3T,那要怎么办能,有两…

路径规划总结(一)

第三讲 路径规划 ps:排版有一些问题&#xff0c;懒得改了&#xff0c;见Github 一、导航规划简介 导航规划&#xff1a;在给定环境的全局或局部知识以及一个或者一系列目标位置的条件下&#xff0c;使机器人能够根据知识和传感器感知信息高效可靠地到达目标位置。 导航规划类…

告别传统FTP!该了解一下替代FTP的新产品了

在某些情况下&#xff0c;需要从服务器上传&#xff08;或下载&#xff09;文件。多年来&#xff0c;最流行的文件传输方法是文件传输协议&#xff08;FTP)。FTP的一大优点是它支持断点续传。FTP收获了方便性&#xff0c;却在安全性上有所欠缺。FTP未加密&#xff0c;这意味着格…

Cache-Augmented Inbatch Importance Resampling for Training Recommender Retriever

目录概符号说明启发本文方法BIR (inbatch importance resampling)XIR (Cache-Augmented Resampling)Chen J., Lian D., Li Y., Wang B., Zheng K. and Chen E. Cache-augmented inbatch importance resampling for training recommender retriever. In Advances in Neural Info…

一条sql了解MYSQL的架构设计

1 前言 对于一个服务端开发来说 MYSQL 可能是他使用最熟悉的数据库工具&#xff0c;然而&#xff0c;大部分的Java工程师对MySQL的了解和掌握程度&#xff0c;大致就停留在这么一个阶段:它可以建库、建表、建索引&#xff0c;然后就是对里面的数据进行增删改查&#xff0c;语句…

MacOS/OSX docker修改已运行容器参数的方法

比如我们刚刚docker run了一个容器,然后里面已经配置了一些信息,装了一些东西,然后我发现我忘记了挂载一个文件夹,怎么修改他们呢? 第一个方法: export容器为镜像再import这个镜像 第二个方法: 把现有的容器提交成镜像,然后重新运行. 以上两种方法都相当于你把一台电…

配置服务器入栈

配置服务器入栈 上回传送门 书接上回 登录我们的服务器管理页面 点击入站列表->点击号 配置如下 注意&#xff1a; 协议是vless 域名是cloudflare上我们设置的二级域名 公钥文件路径就是我们SHH工具上root 文件夹下cret 文件夹下面的证书 公钥名就是我们的证书路径 密钥…

Spring Cloud Alibaba现在还值不值学 ?

6年前面试最常问的并且可以顺利拿到高薪的技能是 Dubbo &#xff0c;2年前面试&#xff0c;只要你简历上有 Spring Cloud 项目的相关经验&#xff0c;肯定会打动面试官&#xff0c;现在呢&#xff1f;恐怕简历上有Dubbo和简单的Spring Cloud技术和经验是无法让面试官高看你的。…

Eureka注册中心以及Ribbon负载均衡

Eureka注册中心 远程调用的问题 1、服务消费者改如何获取服务提供者的地址消息&#xff1f; 2、如果服务提供者有多个&#xff0c;消费者如何进行选择&#xff1f; 3、 消费者如何得知服务提供者的健康状态&#xff1f; Eureka的作用 服务每隔30s给Eureka发送心跳&#xff0c;…

一个技术创业者的自白:三条关于 “选择” 的建议

本文作者 Wyze CTO 刘天强。内容源自「声网开发者创业讲堂第一期」的演讲分享。创业方向:兴趣 VS 趋势 大家在创业的时候首先要选择的是 “做什么”?如何平衡个人特长、兴趣以及风口是创业者面临的难题。我在第一次创业的时候,做了一家主打图像识别 API 的公司 Orbeus,这家…

水电站生态流量下泄监测解决方案

水电站生态流量下泄监测解决方案   一些水电站因下泄生态流量不足造成部分河段减水、脱水甚至干涸的情况&#xff0c;使得群众的生产、生活以及河流的正常生态功能受到了一定程度的影响。计讯物联水电站生态流量下泄监测解决方案精准测量、实时监测、视频监控、预警提醒、及时…