C 中的结构 - 存储、指针、函数和自引用结构

news2024/11/15 11:34:46

0. 结构体的内存分配

当声明某种类型的结构变量时,结构成员被分配连续(相邻)的内存位置。

struct student
    {
        char name[20];
        int roll;
        char gender;
        int marks[5];
    } stu1;

此处,内存将分配给name[20]rollgendermarks[5]st1这意味着or的大小struct student将是其成员大小的总和。不是吗?让我们检查。

void main()
{
    printf("Sum of the size of members = %I64d bytes\n", sizeof(stu1.name) 
    + sizeof(stu1.roll) + sizeof(stu1.gender) + sizeof(stu1.marks));
    printf("Using sizeof() operator = %I64d bytes\n",sizeof(stu1));
}

 /* Output */
Sum of the size of members = 45 bytes
Using sizeof() operator = 48 bytes

使用sizeof()运算符给出的3字节数多于成员大小的总和。为什么?这3个字节在内存中在哪里?我们先回答第二个问题。我们可以打印成员的地址来查找这些3字节的地址。

void main()
{
    printf("Address of member name = %d\n", &stu1.name);
    printf("Address of member roll = %d\n", &stu1.roll);
    printf("Address of member gender = %d\n", &stu1.gender);
    printf("Address of member marks = %d\n", &stu1.marks);
}

 /* Output */
Address of member name = 4225408
Address of member roll = 4225428
Address of member gender = 4225432
Address of member marks = 4225436

图片1

我们观察到该数组marks[5]不是从 分配的,而是4225433从 分配的4224536。但为什么?

数据对齐

在研究数据对齐之前,了解处理器如何从内存读取数据非常重要。

处理器在一个周期内读取一个字。对于32 位处理器,该字为4 字节;对于64 位处理器,该字为 8 字节。周期数越少,CPU 的性能越好。

实现这一目标的一种方法是对齐数据。对齐意味着任何大小的基本数据类型的变量t将始终(默认情况下)具有 的倍数的地址t。这本质上是数据对齐。这种情况每次都会发生。

某些数据类型的对齐地址
数据类型大小(以字节为单位)地址
char11的倍数
short22的倍数
int,float44的倍数
doublelong*(指针)88的倍数
long double1616的倍数
结构填充

可能需要在结构的成员之间插入一些额外的字节以对齐数据。这些额外的字节称为填充

在上面的示例中,3字节充当填充。如果没有它们,marks[0] 类型int(地址为 4 的倍数)的基地址将为4225433(不是 4 的倍数)。

您现在大概可以明白为什么不能直接比较结构了。

图片2

结构成员对齐

为了解释这一点,我们将举另一个例子(你会明白为什么)。

struct example
    {
        int i1;
        double d1;
        char c1;

    } example1;

void main()
{
    printf("size = %I64d bytes\n",sizeof(example1));
}

输出会是什么?让我们应用我们所知道的。

i1是4个字节。其后将填充 4 个字节,因为 的地址应能被 8 整除。对于和d1,其后将分别填充 8 和 1 个字节。因此,输出应为 4 + 4 + 8 + 1 = 17 字节。d1c1

图3

 /* Output */
size = 24 bytes

什么?又错了!如何?通过数组struct example,我们可以更好的理解。我们还将打印 的成员的地址example2[0]

void main()
{
    struct example example2[2];
    printf("Address of example2[0].i1 = %d\n", &example2[0].i1);
    printf("Address of example2[0].d1 = %d\n", &example2[0].d1);
    printf("Address of example2[0].c1 = %d\n", &example2[0].c1);

}

 /* Output */
Address of example2[0].i1 = 4225408
Address of example2[0].d1 = 4225416
Address of example2[0].c1 = 4225424

假设 的大小example2[0]是 17 字节。这意味着 的地址example2[1].i1将为4225425。这不可能的,因为 的地址int应该是 4 的倍数。从逻辑上讲, 的可能地址example2[1].i1似乎是4225428,4 的倍数。

这也是错误的。你发现了吗?now的地址example2[1].d1将是 (28 + 4 ( i1) + 3 ( padding)),4225436它不是 8 的倍数。

为了避免这种不对齐,编译器为每个结构引入了对齐。这是通过在最后一个成员之后添加额外的字节来完成的,称为结构成员对齐

图4

在本节开头讨论的示例中,它不是必需的(因此是另一个示例)。

一个简单的记住方法是通过这个规则 -结构地址和结构长度必须是 的倍数t_max。这里,t_max是结构中成员所占用的最大大小

对于struct example,8 字节是 的最大大小d1。因此,结构末尾有 7 个字节的填充,使其大小为 24 个字节。

这两点将帮助您找到任何结构的大小 -
  1. 任何数据类型都将其值存储在其大小倍数的地址中。

  2. 任何结构的大小都是成员所占用的最大字节数的倍数。

尽管我们可以降低 CPU 周期,但仍会浪费大量内存。将填充量减少到可能的最小值的一种方法是按成员变量大小的递减顺序声明成员变量。

如果我们遵循这一点struct example,结构的大小就会减少到 16 个字节。填充从 7 个字节减少到 3 个字节。

图5

struct example
    {
        double d1; 
        int i1;
        char c1;

    } example3;

void main()
{
    printf("size = %I64d bytes\n",sizeof(example3));
}

 /* Output */
size = 16 bytes
结构填料

包装与填充相反。它防止编译器填充并删除未分配的内存。对于 Windows,我们使用该#pragma pack指令,它指定结构成员的打包对齐方式。

#pragma pack(1)

struct example
    {
        double d1; 
        int i1;
        char c1;

    } example4;

void main()
{
    printf("size = %I64d bytes\n",sizeof(example4));
}

 /* Output */
size = 13 bytes

图6

这可确保成员在 1 字节边界上对齐。换句话说,任何数据类型的地址都必须是 1 字节或其大小(以较小者为准)的倍数。

1. 指针

指针作为成员

结构也可以将指针作为成员。

struct student
    {
        char *name;
        int *roll;
        char gender;
        int marks[5];
    };

void main()
{   int alexRoll = 44;
   struct student stu1 = { "Alex", &alexRoll, 'M', { 76, 78, 56, 98, 92 }};
}

使用.(点运算符),我们可以再次访问成员。由于roll现在有 的地址alexRoll,我们将不得不取消引用stu1.roll来获取值(而不是stu1.(*roll))。

printf("Name: %s\n", stu1.name);
   printf("Roll: %d\n", *(stu1.roll));
   printf("Gender: %c\n", stu1.gender);

   for( int i = 0; i < 5; i++)
    printf("Marks in %dth subject: %d\n", i, stu1.marks[i]);

 /* Output */
Name: Alex
Roll: 43
Gender: M
Marks in 0th subject: 76
Marks in 1th subject: 78
Marks in 2th subject: 56
Marks in 3th subject: 98
Marks in 4th subject: 92
结构体指针

与整数指针、数组指针和函数指针一样,我们也有结构体指针或结构体指针。

struct student {
    char name[20];
    int roll;
    char gender;
    int marks[5];
};

struct student stu1 = {"Alex", 43, 'M', {76, 98, 68, 87, 93}};

struct student *ptrStu1 = &stu1;

在这里,我们声明了一个ptrStu1类型为 的指针struct studentstu1我们已将的地址分配给ptrStu1

ptrStu1存储 的基地址stu1,它是结构体第一个成员的基地址。增加 1 将使地址增加sizeof(stu1)字节。

printf("Address of structure = %d\n", ptrStu1);
printf("Adress of member `name` = %d\n", &stu1.name);
printf("Increment by 1 results in %d\n", ptrStu1 + 1);

/* Output */
Address of structure = 6421968
Adress of member 'name' = 6421968
Increment by 1 results in 6422016

我们可以通过两种方式访问stu1​​using的成员。ptrStu1使用 *(间接运算符)或使用->中缀或箭头运算符)。

对于*,我们将继续使用.(点运算符),而对于 ,->我们将不需要点运算符。

printf("Name w.o using ptrStu1 : %s\n", stu1.name);
printf("Name using ptrStu1 and * : %s\n", (*ptrStu1).name);
printf("Name using ptrStu1 and -> : %s\n", ptrStu1->name);

/* Output */
Name without using ptrStu1: Alex
Name using ptrStu1 and *: Alex
Name using ptrStu1 and ->: Alex

同样,我们也可以访问和修改其他成员。请注意,使用时括号是必需的,*因为点运算符 ( .) 的优先级高于*

结构数组

我们可以创建一个类型数组struct student并使用指针来访问元素及其成员。

struct student stu[10];

 /* Pointer to the first element (structure) of the array */
struct student *ptrStu_type1 = stu;

 /* Pointer to an array of 10 struct student */
struct student (*ptrStu_type2)[10] = &stu;

请注意,ptrStu_type1是 一个指向stu[0]while的指针,而ptrStu_type2是一个指向整个数组 10 的指针struct student。加 1 将ptrStu_type1指向stu[1]

我们可以使用ptrStu_type1循环来遍历元素及其成员。

for( int i = 0; i <  10; i++)
printf("%s, %d\n", ( ptrStu_type1 + i)->name, ( ptrStu_type1 + i)->roll);

2. 功能

作为成员发挥作用

函数不能是结构的成员。但是,使用函数指针,我们可以使用(点运算符)调用函数.。但是,不建议这样做。

 struct example
    {
        int i;
        void (*ptrMessage)(int i);


    };

void message(int);

void message(int i)
{
    printf("Hello, I'm a member of a structure. This structure also has an integer with value %d", i);
}

void main()
{
    struct example eg1 = {6, message};
    eg1.ptrMessage(eg1.i);
}

我们在内部声明了两个成员,一个integeri和一个函数指针。函数指针指向一个接受eger 并返回 的函数。ptrMessagestruct exampleintvoid

message就是这样一个函数。我们eg16和初始化message。然后我们使用和 pass.来调用该函数。ptrMessageeg1.i

结构作为函数参数

与变量一样,我们可以将单个结构成员作为参数传递。

#include <stdio.h>

struct student {
    char name[20];
    int roll;
    char gender;
    int marks[5];
};

void display(char a[], int b, char c, int marks[])
{
    printf("Name: %s\n", a);
    printf("Roll: %d\n", b);
    printf("Gender: %c\n", c);

    for(int i = 0; i < 5; i++)
        printf("Marks in %dth subject: %d\n",i,marks[i]);
}
void main()
{
    struct student stu1 = {"Alex", 43, 'M', {76, 98, 68, 87, 93}};
    display(stu1.name, stu1.roll, stu1.gender, stu1.marks);
}

 /* Output */
Name: Alex
Roll: 43
Gender: M
Marks in 0th subject: 76
Marks in 1th subject: 98
Marks in 2th subject: 68
Marks in 3th subject: 87
Marks in 4th subject: 93

请注意,该结构是在最顶部的struct student外部声明的。main()这是为了确保它在全球范围内可用并且display()可以使用。

如果该结构体在内部定义main(),其范围将被限制为main().

当结构成员数量很大时,传递结构成员的效率不高。然后结构变量可以传递给函数。

void display(struct student a)
{
    printf("Name: %s\n", a.name);
    printf("Roll: %d\n", a.roll);
    printf("Gender: %c\n", a.gender);

    for(int i = 0; i < 5; i++)
        printf("Marks in %dth subject: %d\n",i,a.marks[i]);
}
void main()
{
    struct student stu1 = {"Alex", 43, 'M', {76, 98, 68, 87, 93}};
    display(stu1);
}

如果结构的大小很大,那么传递它的副本不会非常有效。我们可以将结构指针传递给函数。在这种情况下,结构的地址作为实际参数传递。

void display(struct student *p)
{
    printf("Name: %s\n", p->name);
    printf("Roll: %d\n", p->roll);
    printf("Gender: %c\n", p->gender);

    for(int i = 0; i < 5; i++)
        printf("Marks in %dth subject: %d\n",i,p->marks[i]);
}
void main()
{
    struct student stu1 = {"Alex", 43, 'M', {76, 98, 68, 87, 93}};
    struct student *ptrStu1 = &stu1;
    display(ptrStu1);
}

将结构数组传递给函数类似于将任何类型的数组传递给函数。数组的名称,即结构体数组的基地址,被传递给函数。

void display(struct student *p)
{   
    for( int j = 0; j < 10; j++)
   {
       printf("Name: %s\n", (p+j)->name);
        printf("Roll: %d\n", (p+j)->roll);
        printf("Gender: %c\n", (p+j)->gender);

        for(int i = 0; i < 5; i++)
        printf("Marks in %dth subject: %d\n",i,(p+j)->marks[i]);
   }
}

void main()
{
    struct student stu1[10];
    display(stu1);
}
结构作为函数返回

我们可以返回一个结构变量,就像任何其他变量一样。

#include <stdio.h>

struct student {
    char name[20];
    int roll;
    char gender;
    int marks[5];
};


struct student increaseBy5(struct student p)
{
    for( int i =0; i < 5; i++)
        if(p.marks[i] + 5 <= 100)
           {
               p.marks[i]+=5;
           }
    return p;
}

void main()
{
    struct student stu1 = {"Alex", 43, 'M', {76, 98, 68, 87, 93}};
    stu1 = increaseBy5(stu1);

    printf("Name: %s\n", stu1.name);
    printf("Roll: %d\n", stu1.roll);
    printf("Gender: %c\n", stu1.gender);

    for(int i = 0; i < 5; i++)
        printf("Marks in %dth subject: %d\n",i,stu1.marks[i]);
}

 /* Output */
Name: Alex
Roll: 43
Gender: M
Marks in 0th subject: 81
Marks in 1th subject: 98
Marks in 2th subject: 73
Marks in 3th subject: 92
Marks in 4th subject: 98

该函数increaseBy5()对加分后分数小于或等于 100 的科目加 5 分。注意,返回类型是 类型的结构体变量struct student

返回结构成员时,返回类型必须是该成员的类型。

结构体指针也可以由函数返回。

#include <stdio.h>
#include <stdlib.h>

struct rectangle {
    int length;
    int breadth;
};

struct rectangle* function(int length, int breadth)
{
    struct rectangle *p  = (struct rectangle *)malloc(sizeof(struct rectangle));
     p->length = length;
     p->breadth = breadth;
    return p;
}

void main()
{
    struct rectangle *rectangle1 = function(5,4);
    printf("Length of rectangle = %d units\n", rectangle1->length);
    printf("Breadth of rectangle = %d units\n", rectangle1->breadth);
    printf("Area of rectangle = %d square units\n", rectangle1->length * rectangle1->breadth);
}

 /* Output */
Length of rectangle = 5 units
Breadth of rectangle = 4 units
Area of rectangle = 20 square units

struct rectangle请注意,我们已经使用动态分配了 size 的内存malloc()。由于它返回一个void指针,我们必须将其类型转换struct rectangle指针。

3. 自指结构

我们讨论过指针也可以是结构的成员。如果指针是结构体指针怎么办?结构体指针可以与结构体类型相同,也可以不同

自引用结构是那些具有与其成员相同类型的结构指针的结构。

struct student {
    char name[20];
    int roll;
    char gender;
    int marks[5];
    struct student *next;
};

这是一个自引用结构,其中nextstruct student类型结构指针。我们现在将创建两个结构变量stu1stu2用值初始化它们。然后我们将把 的地址存储stu2在 的next成员中stu1

void main()
{
    struct student stu1 = {"Alex", 43, 'M', {76, 98, 68, 87, 93}, NULL};
    struct student stu2 = { "Max", 33, 'M', {87, 84, 82, 96, 78}, NULL};
    stu1.next = &stu2;
}

图7

stu2我们现在可以访问使用stu1和的成员next

void main()
{
    printf("Name: %s\n", stu1.next->name);
    printf("Roll: %d\n", stu1.next->roll);
    printf("Gender: %c\n", stu1.next->gender);

    for(int i = 0; i < 5; i++)
        printf("Marks in %dth subject: %d\n",i,stu1.next->marks[i]);
}

 /* Output */
Name: Max
Roll: 33
Gender: M
Marks in 0th subject: 87
Marks in 1th subject: 84
Marks in 2th subject: 82
Marks in 3th subject: 96
Marks in 4th subject: 78

假设我们想要在 后添加一个不同的结构体变量stu1,即在之间插入另一个结构体变量stu1stu2。这很容易做到。

void main()
{
    struct student stu3 = { "Gasly", 23, 'M', {83, 64, 88, 79, 91}, NULL};
    st1.next = &stu3;
    stu3.next = &stu2;
}

图8

现在stu1.next存储 的地址stu3。并stu3.next有地址stu2。我们现在可以使用访问所有三个结构stu1

  printf("Roll Of %s: %d\n", stu1.next->name, stu1.next->roll);
  printf("Gender Of %s: %c\n", stu1.next->next->name, stu1.next->next->gender);

 /* Output */
Roll Of Gasly: 23
Gender Of Max: M

stu1请注意我们如何使用结构指针在,stu3和之间形成链接stu2我们在这里讨论的内容构成了链表的起点。

自引用结构在创建数据结构(例如链表堆栈队列等)时非常有用。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis学习文档

目录 一、概念1、特征2、关系型数据库和非关系型数据库的区别3、键的结构4、Redis的Java客户端5、缓存更新策略5.1、概念5.2、代码 6、缓存穿透6.1、含义6.2、解决办法6.3、缓存空值代码举例6.4、布隆过滤器代码举例 7、缓存击穿7.1、概念7.2、解决办法7.3、互斥锁代码举例7.4、…

卡码网语言基础课 | 17. 判断集合成员

目录 一、 set 集合 二、 创建集合 2.1 引入头文件 2.2 创建 2.3 插入元素 2.4 删除元素 三、 find的用法 四、 实现基本解题 五、 延伸拓展 题目&#xff1a;编写一个程序&#xff0c;判断给定的整数 n 是否存在于给定的集合中。 输入描述&#xff1a; 有多组测试…

Pycharm中使用matplotlib绘制动态图形

Pycharm中使用matplotlib绘制动态图形 最终效果 最近用pycharm学习D2L时发现官方在jupyter notebook交互式环境中能动态绘制图形&#xff0c;但是在pycharm脚本环境中只会在最终 plt.show() 后输出一张静态图像。于是有了下面这段自己折腾了一下午的代码&#xff0c;用来在pych…

jetson nano SSH远程连接(使用MobaXterm)

文章目录 SSH远程连接1.SSH介绍2.准备工作3.连接步骤3.1 IP查询3.2 新建会话和连接 SSH远程连接 本节课的实现&#xff0c;需要将Jetson Nano和电脑保持在同一个局域网内&#xff0c;也就是连接同一个路 由器&#xff0c;通过SSH的方式来实现远程登陆。 1.SSH介绍 SSH是一种网…

一文讲透Python机器学习特征选择之互信息法

1.互信息法的基本思想 互信息&#xff08;Mutual Information&#xff0c;MI&#xff09;的基本思想是计算每个特征变量与目标变量之间的互信息统计量&#xff0c;互信息统计量衡量变量之间的依赖关系。两个随机变量之间的互信息统计量肯定是非负值&#xff0c;当且仅当两个随…

带键扫的LED专用驱动方案

一、基本概述 TM1650 是一种带键盘扫描接口的LED&#xff08;发光二极管显示器&#xff09;驱动控制专用电路。内部集成有MCU输入输出控制数字接口、数据锁存器、LED 驱动、键盘扫描、辉度调节等电路。TM1650 性能稳定、质量可靠、抗干扰能力强&#xff0c;可适用于24 小时长期…

【强迫症患者必备】SpringBoot项目中Mybatis使用mybatis-redis开启三级缓存必须创建redis.properties优化方案

springboot项目中mybatis使用mybatis-redis开启三级缓存需要创建redis.properties优化方案 前言下载mybatis-redis源码分析RedisCache 代码RedisConfigurationBuilder的parseConfiguration方法 优化改造1.创建JedisConfig类2.复制RedisCache代码创建自定义的MyRedisCache3.指定…

分享超实用的软文撰写步骤!建议收藏

一想到写软文就头大&#xff0c;根本不知道从哪里下手&#xff0c;这是很多写手在创作过程中会遇到的问题。 一篇软文写得好不好&#xff0c;关键就要看你的创作步骤到不到位&#xff0c;软文创作是有一套可执行的具体方式的&#xff0c;跟着步骤来&#xff0c;你也能轻轻松松…

【java扫盲贴】final修饰变量

引用类型&#xff1a;地址不可变 //Java中的引用类型分为类&#xff08;class&#xff09;、接口&#xff08;interface&#xff09;、数组&#xff08;array&#xff09;和枚举&#xff08;enum&#xff09;。//string是特殊的引用类型&#xff0c;他的底层是被final修饰的字…

麒麟操作系统网桥配置

网桥概念&#xff1a; Bridge 是 Linux 上用来做 TCP/IP 二层协议交换的设备&#xff0c;其功能可 以简单的理解为是一个二层交换机或者 Hub&#xff1b;多个网络设备可以连接 到同一个 Bridge&#xff0c;当某个设备收到数据包时&#xff0c;Bridge 会将数据转发 给其他设备。…

osgFX扩展库-刻线特效、立方图镜面高光特效(2)

刻线特效 刻线特效(osgFX::Scribe)是一个双通道的特效&#xff0c;第一个通道以通常的方式渲染图形&#xff0c;第二个通道使用线框模式。用户设置好光照和材质之后&#xff0c;即可使用指定的颜色进行渲染。这个特效使用了PolygonOffset渲染属性类来避免多边形斑驳(Z-fighting…

【C++ Primer Plus学习记录】嵌套循环和二维数组

for循环是一种处理数组的工具。下面进一步讨论如何使用嵌套for循环中来处理二维数组。 C没有提供二维数组类型&#xff0c;但是用户可以创建每个元素本身都是数组的数组。例如&#xff0c;假设要存储5个城市在4年间的最高温度&#xff0c;可以这样声明数组&#xff1a; int m…

机器学习入门(第四天)——朴素贝叶斯

知识树 Knowledge tree P(y|x)&#xff0c;P给定x的条件下&#xff0c;y的概率。如&#xff1a;P(y我招女孩子喜欢的概率|我是学生) 一个小故事 A story 女朋友和妈妈掉河里&#xff0c;路人拿出3颗豆&#xff0c;两颗红豆1颗绿豆。如果我抽中红豆救女朋友&#xff0c;抽中绿…

【用unity实现100个游戏之17】从零开始制作一个类幸存者肉鸽(Roguelike)游戏5(附项目源码)

文章目录 本节最终效果前言能力升级系统绘制升级按钮武器升级道具升级治疗物品效果双手放置武器控制武器移动转向排序默认先隐藏武器&#xff0c;去除默认的武器精灵图片数据联动完善升级界面控制窗口开启关闭时间控制&#xff0c;升级暂停游戏每次升级随机获取属性项目控制等级…

2023/11/24JAVAweb学习(Vue常用指令,Vue.js文件,Ajax,Axios两种请求,Vue-cli脚手架,Vue项目,Element)

age只会执行成立的,show其实都展示了,通过display不展示 使用Vue,必须引入Vue.js文件 假如运行报错,以管理员身份打开vscode,再运行 ------------------------------------------------------------------- 更改端口号

如何有效避免七个常见的身份验证漏洞

引言 随着网络威胁的数量不断增加&#xff0c;了解学习可能会危及到客户在线身份的常见身份验证漏洞就显得格外重要。如果需要在网上满足客户的需求&#xff0c;并使用传统的身份验证机制时&#xff0c;就要对身份验证漏洞保持警惕。 只有了解了这些漏洞&#xff0c;才可以更…

如何配置mybatis中mapper对应关系,解决mybatis报错:Invalid bound statement (not found):

先看一下报错信息&#xff1a; Invalid bound statement&#xff1a;意思是无效的绑定语句 原因就是&#xff1a;在使用mybatis时mapper.xml没有和mapper接口对应起来 解决方式 第一种&#xff1a; 将mapper.xml和mapper接口放在同一位置 在pom中配置&#xff1a; <reso…

C# WPF上位机开发(抽奖程序)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 每到年末或者是尾牙的时候&#xff0c;很多公司都会办一些年终的清楚活动&#xff0c;感谢员工过去一年辛苦的付出。这个时候&#xff0c;作为年会…

专访|OpenTiny 开源社区 常浩:完成比完美更重要

前言 2023年已过大半&#xff0c;备受关注的 OpenTiny*开源之夏活动也顺利结项。开源之夏由中国科学院软件研究所发起的计划&#xff0c;目的在于鼓励在校学生积极参与开源软件的开发维护&#xff0c;推动优秀开源软件社区的繁荣发展。该活动联合各大开源社区&#xff0c;聚焦…

五要素超声波气象站-气象站小百科

随着科技的发展&#xff0c;人们对气象监测的需求也越来越高。为了满足这一需求&#xff0c;一款全新的五要素超声波气象站应运而生。这款气象站不仅具有高精度的测量能力&#xff0c;而且能够实时监测天气变化&#xff0c;为人们提供准确的气象数据。 一、实时监测&#xff0…