首页
游戏
影视
直播
广播
听书
音乐
图片
更多
看书
微视
主播
统计
友链
留言
关于
论坛
邮件
推荐
我的硬盘
我的搜索
我的记录
我的文件
我的图书
我的笔记
我的书签
我的微博
Search
1
科普:Memory Compiler生成的Register file和SRAM有何区别?
40 阅读
2
在IC617中进行xa+vcs数模混仿
33 阅读
3
virtuoso和empyrean alps模拟仿真和混仿教程
32 阅读
4
文档内容搜索哪家强? 15款文件搜索软件横向评测
19 阅读
5
vcs debug rtl或者netlist 中的loop
17 阅读
默认分类
芯片市场
数字电路
芯片后端
模拟电路
芯片验证
原型与样片验证
算法与架构
DFX与量产封装
PC&Server OS设置
移动OS设置
软件方案
新浪备份
有道备份
登录
Search
标签搜索
python
Docker
vcs
PyQT
STM32
cadence
linux
systemverilog
EDA
Alist
vscode
uos
package
C
QT
CXL
sed
sv
webdav
FPGA
bennyhe
累计撰写
341
篇文章
累计收到
31
条评论
首页
栏目
默认分类
芯片市场
数字电路
芯片后端
模拟电路
芯片验证
原型与样片验证
算法与架构
DFX与量产封装
PC&Server OS设置
移动OS设置
软件方案
新浪备份
有道备份
页面
游戏
影视
直播
广播
听书
音乐
图片
看书
微视
主播
统计
友链
留言
关于
论坛
邮件
推荐
我的硬盘
我的搜索
我的记录
我的文件
我的图书
我的笔记
我的书签
我的微博
搜索到
28
篇与
的结果
2025-09-21
C语言枚举end是做什么用的?
最近在知乎上看到一个问题:C语言枚举end是做什么用的?刚开始,我也有一些疑惑,后面查了一些资料,对于这个问题,简单地说一下我的看法。枚举有多大?枚举类型到底有多大,占多少空间呢?这个要具体情况具体分析,编译器会视情况而定。下面是我测试用的编译器版本:gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0Copyright (C) 2017 Free Software Foundation, Inc.This is free software; see the source for copying conditions. There is NOwarranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.当我写下这段代码的时候,实际的输出会是多少呢?有人会说是 1,有人会说是 4,我最终运行的确实是4;▲输出结果但是,这个结果并不是唯一的,它取决于你的编译器,另外还取决于编译器参数,gcc这里有个编译器参数 -fshort-enums,如果我们在编译的时候加上这个,那么编译出来是什么呢?▲短枚举的输出结果最终结果变成了1现在我在原先的代码中,加入CMD_MAX_16BIT = 0xFFFF,下面看看输出结果是多少。▲增带值范围运行输出结果如下:▲输出结果是的,它变成了2。因此,我们可以得出结论就是:编译器将为枚举分配足够的内存大小,来保存我们所声明的任何值。所以,如果我们的代码中只使用低于 256(8位的范围是0~255) 的值,我们的枚举应该是 8 位宽,也就是一个字节,而后面的0xFFFF显然是16位,两个字节,所以最终输出为2为此,我参考了一下gcc user manual,如下;https ://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html-fshort-enumsAllocate to an enum type only as many bytes as it needs for the declared range of possible values. Specifically, the enum type is equivalent to the smallest integer type that has enough room.Warning: the -fshort-enums switch causes GCC to generate code that is not binary compatible with code generated without that switch. Use it to conform to a non-default application binary interface.所以,我们需要明确的是编译器是否会默认执行 -fshort-enums这个命令,大多数是不会的,这里我还测试了一些clang,具体结果和gcc相同。但是,在嵌入式编程中需要注意,这里我查了一下,IAR的编译器默认会执行 -fshort-enums 。电脑上没有IAR,这里我参考了IAR 的 ARM C 编译器的文档IAR C/C++ Development Guide。可以看到enum类型默认的规定,如果要强制为int类型的话,需要编译的时候提那就--enum_is_int的编译参数,如下所示:▲枚举类型所以,这里为了避免编译器的优化,以及不同的硬件平台和不同编译器,从而导致枚举分配内存空间的变化,所以上述增加了一个0xFFFFFFFF,强制编译器为枚举分配4个字节的空间。▲设置最大范围为4字节最终的输出结果都是4,如下图所示:▲输出结果比较看来虽然是一个很小的知识点,但这中间的坑还真不少!好了,本期的文章就到这里了,我们下期再见。
2025年09月21日
1 阅读
0 评论
0 点赞
2025-09-20
MMU那些事儿
EET0P2020年12月27日03:47MMU 诞生之前:在传统的批处理系统如 DOS 系统,应用程序与操作系统在内存中的布局大致如下图:应用程序直接访问物理内存,操作系统占用一部分内存区。操作系统的职责是“加载”应用程序,“运行”或“卸载”应用程序。如果我们一直是单任务处理,则不会有任何问题,也或者应用程序所需的内存总是非常小,则这种架构是不会有任何问题的。然而随着计算机科学技术的发展,所需解决的问题越来越复杂,单任务批处理已不能满足需求了。而且应用程序需要的内存量也越来越大。而且伴随着多任务同时处理的需求,这种技术架构已然不能满足需求了,早先的多任务处理系统是怎么运作的呢?程序员将应用程序分段加载执行,但是分段是一个苦力活。而且死板枯燥。此时聪明的计算机科学家想到了好办法,提出来虚拟内存的思想。程序所需的内存可以远超物理内存的大小,将当前需要执行的留在内存中,而不需要执行的部分留在磁盘中,这样同时就可以满足多应用程序同时驻留内存能并发执行了。从总体上而言,需要实现哪些大的策略呢?所有的应用程序能同时驻留内存,并由操作系统调度并发执行。需要提供机制管理 I/O 重叠,CPU 资源竞争访问。虚实内存映射及交换管理,可以将真实的物理内存,有可变或固定的分区,分页或者分段与虚拟内存建立交换映射关系,并且有效的管理这种映射,实现交换管理。这样,衍生而来的一些实现上的更具体的需求:竞争访问保护管理需求:需要严格的访问保护,动态管理哪些内存页/段或区,为哪些应用程序所用。这属于资源的竞争访问管理需求。高效的翻译转换管理需求:需要实现快速高效的映射翻译转换,否则系统的运行效率将会低下。高效的虚实内存交换需求:需要在实际的虚拟内存与物理内存进行内存页/段交换过程中快速高效。总之,在这样的背景下,MMU 应运而生,也由此可见,任何一项技术的发展壮大,都必然是需求驱动的。这是技术本身发展的客观规律。内存管理的好处为编程提供方便统一的内存空间抽象,在应用开发而言,好似都完全拥有各自独立的用户内存空间的访问权限,这样隐藏了底层实现细节,提供了统一可移植用户抽象。 以最小的开销换取性能最大化,利用 MMU 管理内存肯定不如直接对内存进行访问效率高,为什么需要用这样的机制进行内存管理,是因为并发进程每个进程都拥有完整且相互独立的内存空间。那么实际上内存是昂贵的,即使内存成本远比从前便宜,但是应用进程对内存的寻求仍然无法在实际硬件中,设计足够大的内存实现直接访问,即使能满足,CPU 利用地址总线直接寻址空间也是有限的。内存管理实现总体策略从操作系统角度来看,虚拟内存的基本抽象由操作系统实现完成:处理器内存空间不必与真实的所连接的物理内存空间一致。 当应用程序请求访问内存时,操作系统将虚拟内存地址翻译成物理内存地址,然后完成访问。 从应用程序角度来看,应用程序(往往是进程)所使用的地址是虚拟内存地址,从概念上就如下示意图所示,MMU 在操作系统的控制下负责将虚拟内存实际翻译成物理内存。从而这样的机制,虚拟内存使得应用程序不用将其全部内容都一次性驻留在内存中执行: 节省内存:很多应用程序都不必让其全部内容一次性加载驻留在内存中,那么这样的好处是显而易见,即使硬件系统配置多大的内存,内存在系统中仍然是最为珍贵的资源。所以这种技术节省内存的好处是显而易见的。 使得应用程序以及操作系统更具灵活性。操作系统根据应用程序的动态运行时行为灵活的分配内存给应用程序。使得应用程序可以使用比实际物理内存多或少的内存空间。MMU 以及 TLBMMU(Memory Management Unit)内存管理单元:一种硬件电路单元负责将虚拟内存地址转换为物理内存地址所有的内存访问都将通过 MMU 进行转换,除非没有使能 MMU。TLB(Translation Lookaside Buffer)转译后备缓冲器: 本质上是 MMU 用于虚拟地址到物理地址转换表的缓存这样一种架构,其最终运行时目的,是为主要满足下面这样运行需求:多进程并发同时并发运行在实际物理内存空间中,而 MMU 充当了一个至关重要的虚拟内存到物理内存的桥梁作用。那么,这种框架具体从高层级的概念上是怎么做到的呢?事实上,是将物理内存采用分片管理的策略来实现的,那么,从实现的角度将有两种可选的策略:固定大小分区机制可变大小分区机制固定大小区片机制通过这样一种概念上的策略,将物理内存分成固定等大小的片: 每一个片提供一个基地址 实际寻址,物理地址=某片基址+虚拟地址 片基址由操作系统在进程动态运行时动态加载这种策略实现,其优势在于简易,切换快速。但是该策略也带来明显的劣势: 内部碎片:一个进程不使用的分区中的内存对其他进程而言无法使用 一种分区大小并不能满足所有应用进程所需。可变大小分区机制内存被划分为可变大小的区块进行映射交换管理: 需要提供基址以及可变大小边界,可变大小边界用于越界保护。实 际寻址,物理地址=某片基址+虚拟地址那么这种策略其优势在于没有内部内存碎片,分配刚好够进程所需的大小。但是劣势在于,在加载和卸载的动态过程中会产生碎片。分页机制分页机制采用在虚拟内存空间以及物理内存空间都使用固定大小的分区进行映射管理。从应用程序(进程)角度看内存是连续的 0-N 的分页的虚拟地址空间。物理内存角度看,内存页是分散在整个物理存储中这种映射关系对应用程序不可见,隐藏了实现细节。分页机制是如何寻址的呢?这里介绍的设计理念,具体的处理器实现各有细微差异:虚拟地址包含了两个部分:虚拟页序号 VPN(virtual paging number)以及偏移量虚拟页序号 VPN是页表(Page Table)的索引页表(Page Table)维护了页框号(Page frame number PFN)物理地址由PFN::Offset进行解析。举个栗子,如下图所示:还没有查到具体的物理地址,憋急,再看一下完整解析示例:如何管理页表对于 32 位地址空间而言,假定 4K 为分页大小,则页表的大小为 100MB,这对于页表的查询而言是一个很大的开销。那么如何减小这种开销呢?实际运行过程中发现,事实上只需要映射实际使用的很小一部分地址空间。那么在一级页机制基础上,延伸出多级页表机制。 以二级分页机制为例:单级页表已然有不小的开销,查询页表以及取数,而二级分页机制,因为需要查询两次页表,则将这种开销再加一倍。那么如何提高效率呢?其实前面提到一个概念一直还没有深入描述 TLB,将翻译工作由硬件缓存 cache,这就是 TLB 存在的意义。 TLB 将虚拟页翻译成 PTE,这个工作可在单周期指令完成。TLB 由硬件实现完全关联缓存(并行查找所有条目)缓存索引是虚拟页码缓存内容是 PTE则由 PTE+offset,可直接计算出物理地址TLB 加载谁负责加载 TLB 呢?这里可供选择的有两种策略:由操作系统加载,操作系统找到对应的 PTE,而后加载到 TLB。格式比较灵活。MMU 硬件负责,由操作系统维护页表,MMU 直接访问页表,页表格式严格依赖硬件设计格式。总结一下从计算机大致发展历程来了解内存管理的大致发展策略,如何衍生出 MMU,以及固定分片管理、可变分片管理等不同机制的差异,最后衍生出单级分页管理机制、多级分页管理机制、TLB 的作用。从概念上相对比较易懂的角度描述了 MMU 的诞生、机制,而忽略了处理器的具体实现细节。作为从概念上更深入的理解 MMU 的工作机理的角度,还是不失为一篇浅显易懂的文章。
2025年09月20日
3 阅读
0 评论
0 点赞
2025-09-20
Python 必杀技:用 print() 函数实现的三个特效
print() 应该是初学者最先接触到的第一个 Python 函数,因为几乎所有的启蒙课程都是从 print('Hello world') 开始的。事实上, print() 也是程序员使用频率最高的函数之一,同时也是很多程序员喜欢的代码调试利器。但是关于 print() 函数,你真的了解吗?打字机效果不了解 print() 的 flush 参数,很难实现下图所示的打字机效果:动图封面print() 像个调皮的小朋友,你让他帮你打印,他一定会做,但未必是立即去做,也许会攒够了多个打印任务才执行一次。设置 flush=True,可以让这位小朋友立刻去执行命令。 import time def printer(text, delay=0.2): """打字机效果""" for ch in text: print(ch, end='', flush=True) time.sleep(delay) printer('玄铁重剑,是金庸小说笔下第一神剑,持之则无敌于天下。')旋转式进度指示Linux 系统文本界面下,最常用的进度指示是用横竖斜杠构成的旋转图案。动图封面Python也可以轻松实现这个效果,秘诀就在于 '\b' 字符。 '\b' 相当于键盘上的退格键,可以让我们把刚刚打印过的最后一个字符擦掉重新打印。这个效果,同样需要设置参数 flush 为真。 import time def waiting(cycle=20, delay=0.1): """旋转式进度指示""" for i in range(cycle): for ch in ['-', '\\', '|', '/']: print('\b%s'%ch, end='', flush=True) time.sleep(delay) waiting() 反转字符顺序,就可以改变旋转方向。将第一个字符 '-' 改成 '-- ',还可以实现这样的效果:动图封面覆盖式打印效果'\b' 的作用是回退一个字符,'\r' 则可以退回到行首。借助于 '\r',可以实现整行覆盖式的打印效果:动图封面需要注意的是,整行覆盖的话,新的字符串长度不能小于原字符串长度,否则会留下前一次的打印内容。这个效果,同样需要设置参数 flush 为真。 import time def cover(cycle=100, delay=0.2): """覆盖式打印效果""" for i in range(cycle): s = '\r%d'%i print(s.ljust(3), end='', flush=True) time.sleep(delay) cover()
2025年09月20日
1 阅读
0 评论
0 点赞
2025-09-05
4个令人惊艳的ChatGPT项目,开源了!
1T服务圈儿2023年04月18日17:32江苏来源丨经授权转自 Jack Cui(ID:JackCui-AI)作者丨Jack Cui自从 ChatGPT、Stable Diffusion 发布以来,各种相关开源项目百花齐放,着实让人应接不暇。今天,我将着重挑选几个优质的开源项目,对我们的日常工作、学习生活,都会有很大的帮助。今天整理分享给大家,希望对你有所帮助。一、Visual ChatGPT这个是微软开源的项目,一周多的时间,就斩获了 23.6k+ star。简单概括它,那就是一个多模态的问答系统。支持AI绘画、语言问答、看图问答,将 AI 届近期的 3 大热点集于一身。效果展示:系统实现框架如下:Visual ChatGPT的系统实现框架这是一个“大力出奇迹”的开源项目,集多方研究成果于一身:BLIP、CLIP、ChatGPT、pix2pix、inpainting、vqa 等。说白了,就是教你怎样使用这些项目,搭建一个多模态的问答系统,这个系统架构很有参考价值。项目地址:https://github.com/microsoft/visual-chatgpt二、SadTalker这是一篇 2023 年的 CVPR 论文对应的开源项目。刚刚开源,新鲜热乎~功能就是:根据一张图片、一段音频,合成面部说这段语音的视频。结合 ChatGPT、AIGC、音频文字转换,虚拟二次元 or 三次元形象,就能“活”过来了。此外,还项目还做成了 stable diffusion webui 的插件,也就是直接能在 stable diffusion 里使用。生成的图片,直接配合一段音频,就能生成合成的视频。项目地址:https://github.com/winfredy/sadtalker三、FateZero文本能编辑生成图片?那视频能编辑吗?FateZero:我可以!左图是原图,右图是生成效果,输入的文本是:增加 Pokémon 动漫风格除了视频的风格迁移,也支持修改里面的内容。比如:松鼠是胡萝卜,变成,兔子吃茄子。这个项目也是基于sd做的,离一键生成视频,又进了一步。项目地址:https://github.com/chenyangqiqi/fatezero四、ChatPaperarXiv 想必大家都知道,当下最流行的论文托管网站,上面有来自世界各地的科学家、研究学者。为了提高 arXiv 用户阅读论文的效率,有人开源了一款利用 ChatGPT 总结 arXiv 论文的开源工具 ChatPaper。简而言之,该项目可根据用户关键词下载 arXiv 上的最新论文,利用 ChatGPT3.5 API 强大的归纳能力,将其浓缩成固定格式,文字少且易读。同时,项目支持个人自己部署,或者直接去 Hugge Face 体验。项目地址:https://github.com/kaixindelele/ChatPaperhttps://huggingface.co/spaces/wangrongsheng/ChatPaper来自微信
2025年09月05日
0 阅读
0 评论
0 点赞
2025-09-01
C语言关键字的应用技巧
嵌入式微处理器2022年05月26日12:00北京摘要:嵌入式C开发关键字的应用技巧1、volatilevolatile修饰表示变量是易变的,编译器中的优化器在用到这个变量时必须每次都小心地从内存中重新读取这个变量的值,而不是使用保存在寄存器里的备份,有效的防止编译器自动优化,从而与软件设计相符合。中断服务与主程序共享变量://volatile uint8_t flag=1; uint8_t flag=1; void test(void) { while(flag) { //do something } } //interrupt service routine void isr_test(void) { flag=0; }如果没使用volatile定义flag,可能在优化后test陷入死循环,因为test里使用的flag并没修改它,开启优化后,编译器可能会固定从某个内存取值。例如:for(int i=0; i<100000; i++); //对比 for(volatile int i=0; i<100000; i++);前者可能被优化掉,虽然编码本意是需要执行操作延时,但编译器认为代码无意义。总的来说,volatile是告知编译器,不管代码如何,必须保留,而且使用时需要重新从内存读取更新,不能使用先前读取的缓存,一般在驱动代码中使用较多。2、constconst是恒定不变的意思,其修饰的各种数据类似只读效果。1)、 修饰变量采用const修饰变量,即变量声明为只读,保护变量值以防被修改。例如const int i = 1;上面这个例子表明,变量i具有只读特性,不能够被更改;若想对i重新赋值,如i = 10;属于错误操作。特别说明,定义变量的同时进行初始化,写成int const i=1,是正确的。2)、 修饰数组C语言中const还可以修饰数组,举例如下:const int array[5] = {1,2,3,4,5}; array[0] = array[0]+1; //错误,array是只读的,禁止修改数组元素与变量类似,具有只读属性,不能被更改;一旦更改,编译时就会报错。使用大数组存储固定的信息,例如查表(表驱动法的键值表),可以使用const节省ram。编译器并不给普通const只读变量分配空间,而是将它们保存到符号表中,无需读写内存操作,程序执行效率也会提高。3)、 修饰指针C语言中const修饰指针要特别注意,共有两种形式,一种是用来限定指向空间的值不能修改;另一种是限定指针不可更改。举例如下:int i = 1; int j = 2; const int *p1 = &i; int* const p2 = &j;上面定义了两个指针p1和p2,区别是const后面是指针本身还是指向的内容。在定义1中const限定的是 p1,即其指向空间的值不可改变,若改变其指向空间的值如 p1=10,则程序会报错;但p1的值是可以改变的,对p1重新赋值如p1=&k是没有任何问题的。在定义2中const限定的是指针p2,若改变p2的值如p2=&k,程序将会报错;但 p2,即其所指向空间的值可以改变,如 p2=20是没有问题的,程序正常执行。4)、 修饰函数参数const关键字修饰函数参数,对参数起限定作用,防止其在函数内部被修改。所限定的函数参数可以是普通变量,也可以是指针变量。例如:void fun(const int i) { …… i++; //对i的值进行了修改,程序报错 }常用的函数如strlensize_t strlen(const char *string);const在库函数中使用非常普遍,是一种自我保护的安全编码思维。3、struct与union对于struct 结构体和union共联体在嵌入式领域是使用得非常频繁的,一些可编程芯片提供的寄存器库都是采用结构体和共联体结合的方式来提供给软件人员进行开发,同时在平时的编码过程中这两个数据类型的灵活应用也能够实现代码更好的封装与简化。如下面的简单示例,就可以非常灵活的访问Val中的bit位。 typedef union { BYTE Val; struct __packed { BYTE b0:1; BYTE b1:1; BYTE b2:1; BYTE b3:1; BYTE b4:1; BYTE b5:1; BYTE b6:1; BYTE b7:1; } bits; }BYTE_VAL, BYTE_BITS;其中:1表示按位操作。不只是位-字节可以,单字节与多字节也可以简化拼接。#include "stdio.h" typedef struct { union { struct { unsigned char low; unsigned char high; }; unsigned short result; }; }test_t; int main(int argc, char *argv[]) { test_t hello; hello.high=0x12; hello.low=0x34; printf("result=%04X\r\n",hello.result);//输出 result=1234 return 0; }运行输出 result=1234 (win7系统下QT开发环境),原本需要 (high<<8)|low 运算,可以简化为共用体类型自动完成,但必须注意平台的字节顺序,属于大端还是小端模式。在应用层面,如果明确某个数据可能存在两种可能,而且两种结果不会同时存在,也可以使用结构体与共用体组合的方式,确保模块对外接口统一。例如移动通信模块,使用数据结构保存其基站信息,因为制式不同,模块可能工作在2G-GSM,也可能在4G-Cat1,为保证上层读取基站信息接口唯一,使用共用体就非常合适,否则需定义两套接口。如果觉得文章可以,可关注微信公众号【嵌入式系统】获取更多信息。4、预定义标识符一般编译器都支持预定义标识符,这些标识符结合printf等打印信息帮助程序员调试程序是非常有用的,一般编译器会自动根据用户指定完成替换和处理。部分标识:__FILE__ //表示编译的源文件名 __LINE__ //表示当前文件的行号 __FUNCTION__ //表示函数名 __DATE__ //表示编译日期 __TIME__ //表示编译时间使用范例:printf("file:%s,line:%d,date:%s,time:%s",__FILE__,__LINE__,__DATE__,__TIME__);这些比较常见,主要用于日志分析、版本记录,便于调试。5、#与###:是一种运算符,用于带参宏的文本替换,将跟在后面的参数转成一个字符串常量。 ##:是一种运算符,是将两个运算对象连接在一起,也只能出现在带参宏定义的文本替换中。#include "stdio.h" #define TO_STR(s) #s #define COMB(str1,str2) str1##str2 int main(int argc, char *argv[]) { int UART0= 115200; printf("UART0=%d\n", COMB(UART, 0));//字符串合并为变量UART0 printf("%s\n", TO_STR(3.14));//将数字变成字符串 return 0; }6、void 与 void*void表示的是无类型,不能声明变量或常量,但是可以把指针定义为void类型,如void ptr。void 指针可以指向任意类型的数据,在C语言指针操作中,任意类型的数据地址都可转为void* 指针。因为指针本质上都是unsigned int。常用的内存块操作库函数:void * memcpy( void *dest, const void *src, size_t len ); void * memset( void *buffer, int c, size_t num);数据指针为void* 类型,对传入任意类型数据的指针都可以操作。另外其中memcpy第二个参数,const现在也如前文所述,拷贝时对传入的原数据内容禁止修改。特殊说明,指针是不能使用sizeof求内容大小的,在ARM系统固定为int 4字节。对于函数无输入参数的,也尽量加上void,如void fun(void);7、weak一般简化定义#define _WEAK __attribute__((weak)) 函数名称前面加上__WEAK属性修饰符称为“弱函数”,类似C++的虚函数。链接时优先链接为非weak定义的函数,如果找不到则再链接带weak函数。_WEAK void fun(void) { //do this } //不在同一个.c,两同名函数不能在同一个文件 void fun(void) { //do that } 这种自动选择的机制,在代码移植和多模块配合工作的场景下应用较多。例如前期移植代码,需要调用某个接口fun,但当前该接口不存在或者未移植完整使用,可以使用weak关键字定义为空函数先保证编译正常。后续移植完成实现了fun,即软件中有2个fun函数没有任何错误,编译器自动会识别使用后者。当然也粗暴的#if 0屏蔽对fun的调用,但要确保后续记得放开。8、总结存在即合理,C语言里面的关键字,每个都有其特殊的意义,只是一般使用较少,譬如作文,使用常用的汉字可以;但引经据典,使用特殊的修饰辞藻更能显出水平。后续对section 进行详细说明,它和动态加载(OTA)、接口自启动相关。来自微信
2025年09月01日
0 阅读
0 评论
0 点赞
1
2
...
6