C语言进阶第九课 --------动态内存管理
作者前言
🎂 ✨✨✨✨✨✨🍧🍧🍧🍧🍧🍧🍧🎂
🎂 作者介绍: 🎂🎂
🎂 🎉🎉🎉🎉🎉🎉🎉 🎂
🎂作者id:老秦包你会, 🎂
简单介绍:🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂
喜欢学习C语言和python等编程语言,是一位爱分享的博主,有兴趣的小可爱可以来互讨 🎂🎂🎂🎂🎂🎂🎂🎂
🎂个人主页::小小页面🎂
🎂gitee页面:秦大大🎂
🎂🎂🎂🎂🎂🎂🎂🎂
🎂 一个爱分享的小博主 欢迎小可爱们前来借鉴🎂
动态内存
- **作者前言**
- 动态内存的意义
- 动态内存函数的介绍
- malloc
- free
- calloc
- realloc
- 常见的动态内存错误
- 对NULL指针的解引用操作
- 对动态内存的越界访问
- 对非动态开辟的内存进行free释放
- 使用free释放动态开辟的内存的一部分
- 对一块空间多次释放
- 动态开辟内存忘记释放(内存泄漏)
- 经典笔试题
- C/C++中程序内存区域分布
- 柔性数组
- 柔性数组的特点
- 使用柔性数组的好处
- 总结
动态内存的意义
在我们空间的开辟有定义变量
#include<stdio.h>
int main()
{int a = 0;char arr[] = "sdsd";printf("%p\n", &a);printf("%p\n", arr);return 0;
}
甚至还有局部变量都会向内存申请空间,但是这些空间不擦长久,或者很难随意操作这些空间进行销毁,或者不能随意扩大空间
但是上述的开辟空间的方式有两个特点:
- 空间开辟大小是固定的。
- 数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配。
动态内存函数的介绍
malloc

这个函数的动态内存开辟函数,
size_t是一个无符号整数类型,表示需要分配的内存大小(以字节为单位)。malloc函数会在堆上分配一块指定大小的内存,并返回指向该内存块的指针。如果分配失败,则返回NULL。
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
int main()
{int* arr = (int*)malloc(sizeof(int) * INT_MAX);if (arr == NULL){perror("malloc:");return 1;}return 0;
}
可能有一些人会利用malloc申请0个字节,这种做法本身没有错误,但是这个就是脱裤子放屁多此一举
如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器。

free
有心细的小可爱就会发现,当我们程序运行结束就会释放出开辟的动态内存(被动),如果我们要自己释放就要用到free函数

free是C语言中用于释放动态分配内存的函数。当我们使用malloc、calloc等函数在程序运行期间动态申请内存时,需要在使用完毕后使用free函数将其释放回系统,以便其他程序或进程可以使用。
其中,ptr是指向需要释放的内存块的指针。使用free函数可以将之前通过malloc、calloc等函数动态分配的内存块释放回系统,以避免内存泄漏和浪费。
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
int main()
{int* arr = (int*)malloc(sizeof(int) * 10);if (arr == NULL){perror("malloc");return 1;}int i = 0;for (i = 0; i < 10; i++){arr[i] = i;printf("%d ", arr[i]);}free(arr);arr = NULL;return 0;
}
需要注意的是,在使用free函数释放内存块时需要注意以下几点:
只能释放之前通过malloc、calloc等函数动态分配的内存块,不能释放栈上的局部变量和全局变量等静态分配的内存;
不能重复释放同一块内存,否则会导致程序崩溃;
释放内存后,应该将指针设置为NULL,以避免出现野指针问题。
calloc

calloc是C语言中用于动态分配内存并进行初始化的函数。与malloc函数相似,calloc也用于在程序运行时动态分配内存,但与malloc不同的是,calloc会将分配的内存块初始化为零,而malloc不会。
其中,num表示需要分配的元素个数,size表示每个元素的大小。calloc函数会分配num个大小为size的连续内存块,并将其初始化为零。如果分配成功,则返回指向分配内存块的指针;如果分配失败,则返回NULL。
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
int main()
{int* arr = (int*)calloc(10, sizeof(int));if (arr == NULL){perror("calloc");return 1;}int i = 0;for (i = 0; i < 10; i++){arr[i] = i;}free(arr);arr = NULL;return 0;
}
realloc
realloc函数的出现让动态内存管理更加灵活,前面的malloc和calloc申请的空间无法避免申请空间过大或者过小,realloc函数就可以做到动态开辟内存的大小进行调整
realloc是C语言中用于重新分配内存的函数。在程序运行期间,我们有时需要重新分配内存块的大小,可以使用realloc函数来实现。realloc函数可以将之前分配的内存块的大小调整为新的大小,并返回指向新内存块的指针。

ptr是指向之前分配的内存块的指针,size是需要重新分配的内存块的大小。
如果重新分配成功,则返回指向新内存块的指针;如果分配失败,则返回NULL。需要注意的是,如果新的大小小于原来的大小,则realloc函数会截断原来的内存块;如果新的大小大于原来的大小,则realloc函数会在原来的内存块后面分配新的内存块。
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
int main()
{int* arr = (int*)malloc(sizeof(int) * 10);int* arr1 = (int*)calloc(20, sizeof(int));if (arr == NULL){perror("malloc");}if (arr1 == NULL){perror("calloc");}//调整空间int *arr3 = (int*)realloc(arr, 2000 * sizeof(int));if (arr3 != NULL){arr = arr3;}free(arr);free(arr1);arr = NULL;arr1 = NULL;arr3 = NULL;return 0;
}
realloc函数开辟的空间情况分两种:
第一种:

之前malloc开辟空间的后面刚好满足扩大后的空间大小就会在原来的地址处重新扩大,这种情况返回的新地址就会和旧地址的值是一样的

第二种:

之前malloc开辟的空间的后面不能满足扩大后的空间大小,就会放弃在原来的空间扩大,反而会重新找一块能满足条件的内存进行开辟,一次性开辟好,会将旧的空间的数据就会拷贝到新的空间,旧的空间就会销毁

常见的动态内存错误
对NULL指针的解引用操作
这种情况的出现是因为没有对返回值进行判断造成的
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
int main()
{int* arr = malloc(sizeof(int) * INT_MAX);int i = 0;for (i = 0; i < INT_MAX; i++){arr[i] = 1;}return 0;
}
如上面代码一样,没有判断是否创建成功,直接解引用操作就会报错

对动态内存的越界访问
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
int main()
{int* arr = (int*)calloc(10, sizeof(int));if (arr == NULL){perror("calloc");return 1;}int i = 0;for (i = 0; i <= 10; i++){arr[i] = i;}for (i = 0; i <= 10; i++){printf("%d ", *(arr + i));}free(arr);arr = NULL;return 0;
}
这种情况就和我们创建数组,越界访问是一样的
对非动态开辟的内存进行free释放
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
int main()
{int a = 0;int* p = &a;free(p);p = NULL;return 0;
}
这种情况就是懵了
使用free释放动态开辟的内存的一部分
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
int main()
{int* arr = calloc(10, sizeof(int));int i = 0; for (i = 0; i < 5; i++){*arr = i;arr++;}free(arr);arr = NULL;return 0;
}

对一块空间多次释放
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
int main()
{int* arr = (int*)malloc(sizeof(int) * 10);if (arr == NULL){perror("malloc");return 1;}free(arr);free(arr);return 0;
}
解决这个错误我们可以养成一个习惯,只要释放过的空间,对应的变量全部赋值为NULL,因为free释放NULL不会有啥错误
动态开辟内存忘记释放(内存泄漏)
include <stdio.h>
#include <stdlib.h>
#include <limits.h>
void Other()
{int* arr = (int*)malloc(sizeof(int) * 10);
}
int main()
{Other();while (1){;}return 0;
}
这样很容易造成空间不足,我们要记住谁申请,谁释放,如果没有释放交接任务也要告诉别人释放
经典笔试题
第一题
void GetMemory(char *p)
{p = (char *)malloc(100);
}
void Test(void)
{char *str = NULL;GetMemory(str);strcpy(str, "hello world");printf(str);
}
上面这些代码主要的错误就是当我们调用Test函数时,也会调用到 GetMemory函数,而传参传递的时变量str的值,而不是地址,最后str的值还是NULL,还造成了内存泄漏,对NULL解引用就会报错,如果把 GetMemory函数更改为 GetMemory(char* *p ),然后传递str的地址
更改后:
void GetMemory(char* *p)
{*p = (char*)malloc(100);
}
void Test(void)
{char* str = NULL;GetMemory(&str);strcpy(str, "hello world");printf(str);free(str);str = NULL;
}
int main()
{Test();return 0;
}
还要注意的printf函数的使用没有错,是可以这样写的,这个写法只针对字符串
第二题
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
char* GetMemory(void)
{char p[] = "hello world";return p;
}
void Test(void)
{char* str = NULL;str = GetMemory();printf(str);
}
int main()
{Test();return 0;
}
这里的错误是GetMemory函数在调用结束后,p的空间销毁掉了,返回的值也就成了野指针
第三题
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include<string.h>
void GetMemory(char** p, int num)
{*p = (char*)malloc(num);
}
void Test(void)
{char* str = NULL;GetMemory(&str, 100);strcpy(str, "hello");printf(str);
}
int main()
{Test();return 0;
}
这道题主要是少了free释放,内存泄漏了
第四题
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include<string.h>
void Test(void)
{char* str = (char*)malloc(100);strcpy(str, "hello");free(str);if (str != NULL){strcpy(str, "world");printf(str);}
}
int main()
{Test();return 0;
}
这里的错误就是野指针,在我们释放了动态开辟的内存,str还是指向那块地址,这样就会非法访问内存,这里我们要注意释放后一定要赋值NULL
C/C++中程序内存区域分布

其中代码段是用来存储代码翻译成的二进制指令和只读常量(字符串常量),里面的数据不可更改

这个图就可以很明了的知道动态内存开辟的空间是在堆区,局部变量和形式参数的空间是在栈区m全局变量和静态变量的空间是在数据段(静态区)
还要注意的是函数的空间也是在栈上创建的,函数栈帧的创建和销毁前面我已经写过,可以去看看
C/C++程序内存分配的几个区域:
- 栈区(stack):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结
束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是
分配的内存容量有限。 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返
回地址等。 - 堆区(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。分
配方式类似于链表。 - 数据段(静态区)(static)存放全局变量、静态数据。程序结束后由系统释放。
- 代码段:存放函数体(类成员函数和全局函数)的二进制代码。
柔性数组
这个概念是在C99引入的,之前是没有的
形成柔性数组的条件:
- 要在结构体中
- 是结构体的最后一个成员,并且这个数组是一个未知大小的数组(变长数组)
写法:
struct S{int a;int arr[];//柔性数组}a1;struct S1{int a;int arr[0];//柔性数组}a2;
代码中 的两个结构体属于两种写法,
柔性数组的特点
- 结构中的柔性数组成员前面必须至少一个其他成员。
- sizeof 返回的这种结构大小不包括柔性数组的内存。
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include<string.h>
struct S1
{int a;int arr[0];
};
int main()
{printf("%d", sizeof(struct S1);return 0;
}

可以看到sizeof返回的大小不包含柔性数组,假设结构体只有一个成员,而这个成员是柔性数组就会发现这个结构体的大小是0
3. 包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include<string.h>
struct S1
{int a;int arr[0];
};
int main()
{struct S1* p = (struct S1*)malloc(sizeof(int) + 20);if (p == NULL){perror("malloc");return;}p->a = 10;int i = 0;for (i = 0; i < 4; i++){p->arr[i] = i;}free(p);p = NULL;return 0;
}
其实我们可以不使用柔性数组,我们也可以模拟出来
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include<string.h>
struct S1
{int a;int arr[0];
};
struct S2
{int a;int* arr;
};
int main()
{/*struct S1* p = (struct S1*)malloc(sizeof(int) + 20);if (p == NULL){perror("malloc");return;}p->a = 10;int i = 0;for (i = 0; i < 4; i++){p->arr[i] = i;}free(p);p = NULL;*/struct S2 a2;a2.a = 10;a2.arr = (int*)malloc(sizeof(int) * 4);if (a2.arr == NULL){perror("malloc");return;}printf("%d", sizeof(struct S2));free(a2.arr);a2.arr = NULL;return 0;
}
使用这种方法虽然可以代替柔性数组,但是使用free的次数会增多,而使用柔性数组只需使用一次free
当我们使用malloc开辟的数次越多,产生的内存碎片就会越多,内存的利用率就会下降
使用柔性数组的好处
第一个好处是:方便内存释放
如果我们的代码是在一个给别人用的函数中,你在里面做了二次内存分配,并把整个结构体返回给用户。用户调用free可以释放结构体,但是用户并不知道这个结构体内的成员也需要free,所以你不能指望用户来发现这个事。所以,如果我们把结构体的内存以及其成员要的内存一次性分配好了,并返回给用户一个结构体指针,用户做一次free就可以把所有的内存也给释放掉。
第二个好处是:这样有利于访问速度.
连续的内存有益于提高访问速度,也有益于减少内存碎片。(其实,我个人觉得也没多高了,反正你跑不了要用做偏移量的加法来寻址)
总结
在这里介绍了动态内存的开辟、常见错误柔性数组,有不懂的小可爱可以私聊
相关文章:
C语言进阶第九课 --------动态内存管理
作者前言 🎂 ✨✨✨✨✨✨🍧🍧🍧🍧🍧🍧🍧🎂 🎂 作者介绍: 🎂🎂 🎂 🎉🎉🎉…...
嵌入式 Tomcat 调校
SpringBoot 嵌入了 Web 容器如 Tomcat/Jetty/Undertow,——这是怎么做到的?我们以 Tomcat 为例子,尝试调用嵌入式 Tomcat。 调用嵌入式 Tomcat,如果按照默认去启动,一个 main 函数就可以了。 简单的例子 下面是启动…...
初始化固定长度的数组
完全解析Array.apply(null,「length: 1000」) 创建固定长度数组,并且初始化值。直接可以使用map、forEach、reduce等有遍历性质的方法。 如果直接使用Array(81),map里面的循环不会执行。 //方法一 Array.apply(null, { length: 20 })//方法二 Array(81)…...
实现基于 Jenkins 的多服务器打包方案
实现基于 Jenkins 的多服务器打包方案 在实际项目中,我们经常会遇到需要将一个应用程序或服务部署到不同的服务器上的需求。而使用 Jenkins 可以很方便地自动化这个过程。 设置参数 首先,我们需要设置一些参数,以便在构建过程中指定要部署…...
探索现代IT岗位:职业机遇的海洋
目录 1 引言2 传统软件开发3 数据分析与人工智能4 网络与系统管理5 信息安全6 新兴技术领域 1 引言 随着现代科技的迅猛发展,信息技术(IT)行业已经成为了全球经济的关键引擎,改变了我们的生活方式、商业模式和社会互动方式。IT行…...
np.linspace精确度
前言 今天发现一个大坑,如果是序列是小数的话,不要用np.linspace,而要用np.arrange指定等差序列。比如入下图中a和b是一样的意思,但是b是有较大误差的。 anp.arange(0,4,0.4) bnp.linspace(0,4,10) print("a",a) prin…...
GD32_定时器输入捕获波形频率
GD32_定时器输入捕获波形频率(多通道轮询) 之前项目上用到一个使用定时器捕获输入采集风扇波形频率得到风扇转速的模块,作为笔记简单记录以下当时的逻辑结构和遇到的问题,有需要参考源码、有疑问或需要提供帮助的可以留言告知 。…...
单窗口单IP适合炉石传说游戏么?
游戏道具制作在炉石传说中是一个很有挑战的任务,但与此同时,它也是一个充满机遇的领域。在这篇文章中,我们将向您展示如何在炉石传说游戏中使用动态包机、多窗口IP工具和动态IP进行游戏道具制作。 作者与主题的关系:作为一名热爱炉…...
win11安装docekr、docker-compose
1.docker安装 下载地址:Install Docker Desktop on Windows | Docker Docs 出问题别慌,看清楚提示信息,cmd更新wsl,什么是wsl,百度好好理解一下哦 2.docker-compose安装 还是去官方看看怎么说的,然后跟着处…...
Postman的简单使用
Postman简介 官网 Postman是Google公司开发的一款功能强大的网页调试与发送HTTP请求,并能运行测试用例的Chrome插件 使用Postman进行简单接口测试 新建测试 → 选择请求方式 → 请求URL,下面用百度作为例子: 参考文档 [1] Postman使用教程…...
信号继电器驱动芯片(led驱动芯片)
驱动继电器需要配合BAV99(防止反向脉冲)使用 具体应用参考开源项目 电阻箱 sbstnh/programmable_precision_resistor: A SCPI programmable precision resistor (github.com) 这个是芯片的输出电流设置 对应到上面的实际开源项目其设置电阻为1.5K&…...
IDEA配置HTML和Thymeleaf热部署开发
IDEA配置HTML和Thymeleaf热部署开发 1.项目配置2. IDEA配置3. 使用 需求:现在我们在开发不分离项目的时候(SpringBootThmeleaf)经常会改动了类或者静态html文件就需要重启一下服务器, 这样不仅时间开销很大,而且经常重…...
Nginx动静分离
为了加快网站的解析速度,可以把动态页面和静态页面由不同的服务器来解析,加快解析速度。降低原来单个服务器的压力。 在动静分离的tomcat的时候比较明显,因为tomcat解析静态很慢,其实这些原理的话都很好理解,简单来说&…...
Spring中AOP详解
目录 一、AOP的概念 二、AOP的底层实现原理 2.1 JDK的动态代理 2.1.1 invocationhandler接口 2.1.2 代理对象和原始类实现相同的接口 interfaces 2.1.3 类加载器ClassLoador 2.1.4 编码实现 2.2 Cglib动态代理 2.2.1 Cglib动态代理编码实现 三、AOP如何通过原始对象的id获取到代…...
Unity DOTS系列之Filter Baking Output与Prefab In Baking核心分析
最近DOTS发布了正式的版本, 我们来分享一下DOTS里面Baking核心机制,方便大家上手学习掌握Unity DOTS开发。今天给大家分享的Baking机制中的Filter Baking Output与Prefab In Baking。 对啦!这里有个游戏开发交流小组里面聚集了一帮热爱学习游戏的零基础…...
Matlab读写操作
随机生成一个3*3矩阵,对矩阵进行按列升序排列 >> Arand(3,3); >> [B, ~] sort(A, 2); >> B B 0.4898 0.6797 0.70940.4456 0.6551 0.75470.1626 0.2760 0.6463在不同数值类型下显示π的值 1、默认数值类型 >> p_defa…...
Android 开发技巧:音乐播放器的后台处理【Service、Handler、MediaPlayer】
给定部分完成的MusicPlayer项目,实现其中未完成的service部分: 1、创建MusicService类,通过service组件实现后台播放音乐的功能; 2、在MainActivity中通过ServiceConnection连接MusicService,实现对音乐播放的控制&…...
使用Windows平台的Hyper-V虚拟机安装CentOS7的详细过程
Hyper-V虚拟机安装CentOS7 前言常见Linux系统CentOSUbuntuDebianKaliFedoraArch LinuxMintManjaroopenSUSE Hyper-V开启Hyper-V打开Hyper-V Hyper-V的使用新建虚拟机开始安装分区配置开始安装 修改yum源为阿里源 前言 作为一名开发者,就服务器而言,接触最…...
某马机房预约系统 C++项目(二) 完结
8.4、查看机房 8.4.1、添加机房信息 根据案例,我们还是先在computerRoom.txt中直接添加点数据 //几机房 机器数量 1 20 2 50 3 1008.4.2、机房类创建 同样我们在头文件下新建一个computerRoom.h文件 添加如下代码: #pragma once #include<i…...
npm 安装到指定文件夹
创建一个文件夹,用vscode或者cmd打开, 执行 npm install --prefix ./ 路径 包名, npm install --prefix ./ 包名 , 就会将包安装在当前文件夹, 例如: npm install --prefix ./ -g oppo-minigame…...
rk3576 点亮 LCD(mipi)
rk3576 适配 mipi 屏 瑞芯微 RK3576 是一款面向中高端 AIoT 市场的 SoC,其 MIPI DSI (Display Serial Interface) 接口在性能和灵活性上相比前代(如 RK3399/RK3568)有显著提升,特别是在物理层协议的支持上更加现代化。相比RK3399 RK3568的mipi 接口少了 8lane,但是RK3576…...
革新性英雄联盟智能辅助解决方案:一站式游戏体验提升工具
革新性英雄联盟智能辅助解决方案:一站式游戏体验提升工具 【免费下载链接】League-Toolkit 兴趣使然的、简单易用的英雄联盟工具集。支持战绩查询、自动秒选等功能。基于 LCU API。 项目地址: https://gitcode.com/gh_mirrors/le/League-Toolkit 在快节奏的英…...
Hunyuan-MT-7B开源镜像免配置部署:像素语言传送门一键启动教程(含GPU适配)
Hunyuan-MT-7B开源镜像免配置部署:像素语言传送门一键启动教程(含GPU适配) 1. 项目介绍 像素语言跨维传送门是一款基于Tencent Hunyuan-MT-7B大模型构建的创新翻译工具。它将传统翻译体验重构为16-bit像素冒险风格,让语言转换变…...
【手把手实战!fMRI数据预处理全流程解析】SPM12操作指南
1. fMRI数据预处理入门:为什么需要SPM12? 第一次接触fMRI数据分析的朋友,往往会被各种专业术语吓到——DICOM、NIFTI、头动校正、空间标准化...这些名词听起来就让人头大。但别担心,就像我第一次在实验室处理数据时导师说的&…...
基于摄像头和网络的火灾监测系统开源项目推荐
推荐的开源项目 基于YOLOv10的火焰烟雾检测系统(最推荐) 特点:支持图像、视频和摄像头实时检测,提供完整的GUI界面(PySide6开发)优势:界面简洁易用,代码结构清晰,适合初学…...
小觅相机‘凉了’之后,我们如何用它的SDK和开源工具链构建自己的SLAM数据集?
从废弃硬件到研究利器:小觅相机SDK与开源工具链的SLAM数据集构建指南 当一款硬件产品的厂商突然消失,官网关闭、技术支持中断,那些被遗弃的设备往往会被贴上"电子垃圾"的标签。但作为一名SLAM研究者或爱好者,你是否想过…...
为什么选择ODB++格式?Cadence与HyperLynx数据交换的最佳实践
为什么选择ODB格式?Cadence与HyperLynx数据交换的最佳实践 在高速PCB设计领域,数据格式的选择直接影响着设计到制造的整个流程效率。当工程师需要在Cadence Allegro和HyperLynx之间传递设计数据时,ODB正逐渐成为行业首选。这种智能数据格式不…...
Android tinyalsa深度解析之pcm_params_get_periods_min调用流程与实战(一百七十三)
简介: CSDN博客专家、《Android系统多媒体进阶实战》作者 博主新书推荐:《Android系统多媒体进阶实战》🚀 Android Audio工程师专栏地址: Audio工程师进阶系列【原创干货持续更新中……】🚀 Android多媒体专栏地址&a…...
Android tinyalsa深度解析之pcm_params_get_period_size_max调用流程与实战(一百七十二)
简介: CSDN博客专家、《Android系统多媒体进阶实战》作者 博主新书推荐:《Android系统多媒体进阶实战》🚀 Android Audio工程师专栏地址: Audio工程师进阶系列【原创干货持续更新中……】🚀 Android多媒体专栏地址&a…...
无噪音RS1 ROSAHL 电解式除湿器 3D 打印耗材盒/户外摄像头/激光器精准除湿设备
RS1 是 ROSAHL(日本 Ryosai Technica 生产)推出的一款超紧凑型电解式除湿器,采用全球领先的固体聚合物电解质(SPE)膜技术,通过电化学原理主动将密闭空间内的水分子分解并以气态形式排出。它具备无噪音、无振…...
