鸿蒙内核源码分析(VFS篇) | 文件系统和谐共处的基础

基本概念 | 官方定义

VFS(Virtual File System)是文件系统的虚拟层,它不是一个实际的文件系统,而是一个异构文件系统之上的软件粘合层,为用户提供统一的类Unix文件操作接口。由于不同类型的文件系统接口不统一,若系统中有多个文件系统类型,访问不同的文件系统就需要使用不同的非标准接口。而通过在系统中添加VFS层,提供统一的抽象接口,屏蔽了底层异构类型的文件系统的差异,使得访问文件系统的系统调用不用关心底层的存储介质和文件系统类型,提高开发效率。

OpenHarmony内核中,VFS框架是通过在内存中的树结构来实现的,树的每个结点都是一个Vnode结构体,父子结点的关系以PathCache结构体保存。VFS最主要的两个功能是:

  • 查找节点。
  • 统一调用(标准)。

VFS层具体实现包括四个方面:

  • 通过三大函数指针操作接口,实现对不同文件系统类型调用不同接口实现标准接口功能;
  • 通过VnodePathCache机制,提升路径搜索以及文件访问的性能;
  • 通过挂载点管理进行分区管理;
  • 通过FD管理进行进程间FD隔离等。

三大操作接口

VFS层通过函数指针的形式,将统一调用按照不同的文件系统类型,分发到不同文件系统中进行底层操作。各文件系统的各自实现一套Vnode操作(VnodeOps)、挂载点操作(MountOps)以及文件操作接口(file_operations_vfs),并以函数指针结构体的形式存储于对应Vnode、挂载点、File结构体中,实现VFS层对下访问。这三个接口分别为:

VnodeOps | 操作 Vnode 节点
struct VnodeOps {int (*Create)(struct Vnode *parent, const char *name, int mode, struct Vnode **vnode);//创建节点int (*Lookup)(struct Vnode *parent, const char *name, int len, struct Vnode **vnode);//查询节点//Lookup向底层文件系统查找获取inode信息int (*Open)(struct Vnode *vnode, int fd, int mode, int flags);//打开节点int (*Close)(struct Vnode *vnode);//关闭节点int (*Reclaim)(struct Vnode *vnode);//回收节点int (*Unlink)(struct Vnode *parent, struct Vnode *vnode, const char *fileName);//取消硬链接int (*Rmdir)(struct Vnode *parent, struct Vnode *vnode, const char *dirName);//删除目录节点int (*Mkdir)(struct Vnode *parent, const char *dirName, mode_t mode, struct Vnode **vnode);//创建目录节点/*创建一个目录时,实际做了3件事:在其“父目录文件”中增加一个条目;分配一个inode;再分配一个存储块,用来保存当前被创建目录包含的文件与子目录。被创建的“目录文件”中自动生成两个子目录的条目,名称分别是:“.”和“..”。前者与该目录具有相同的inode号码,因此是该目录的一个“硬链接”。后者的inode号码就是该目录的父目录的inode号码。所以,任何一个目录的"硬链接"总数,总是等于它的子目录总数(含隐藏目录)加2。即每个“子目录文件”中的“..”条目,加上它自身的“目录文件”中的“.”条目,再加上“父目录文件”中的对应该目录的条目。*/int (*Readdir)(struct Vnode *vnode, struct fs_dirent_s *dir);//读目录节点int (*Opendir)(struct Vnode *vnode, struct fs_dirent_s *dir);//打开目录节点int (*Rewinddir)(struct Vnode *vnode, struct fs_dirent_s *dir);//定位目录节点int (*Closedir)(struct Vnode *vnode, struct fs_dirent_s *dir);//关闭目录节点int (*Getattr)(struct Vnode *vnode, struct stat *st);//获取节点属性int (*Setattr)(struct Vnode *vnode, struct stat *st);//设置节点属性int (*Chattr)(struct Vnode *vnode, struct IATTR *attr);//改变节点属性(change attr)int (*Rename)(struct Vnode *src, struct Vnode *dstParent, const char *srcName, const char *dstName);//重命名int (*Truncate)(struct Vnode *vnode, off_t len);//缩减或扩展大小int (*Truncate64)(struct Vnode *vnode, off64_t len);//缩减或扩展大小int (*Fscheck)(struct Vnode *vnode, struct fs_dirent_s *dir);//检查功能int (*Link)(struct Vnode *src, struct Vnode *dstParent, struct Vnode **dst, const char *dstName);int (*Symlink)(struct Vnode *parentVnode, struct Vnode **newVnode, const char *path, const char *target);ssize_t (*Readlink)(struct Vnode *vnode, char *buffer, size_t bufLen);
};

MountOps | 挂载点操作

//挂载操作
struct MountOps {int (*Mount)(struct Mount *mount, struct Vnode *vnode, const void *data);//挂载int (*Unmount)(struct Mount *mount, struct Vnode **blkdriver);//卸载int (*Statfs)(struct Mount *mount, struct statfs *sbp);//统计文件系统的信息,如该文件系统类型、总大小、可用大小等信息
};
file_operations_vfs | 文件操作接口
struct file_operations_vfs 
{int     (*open)(struct file *filep);	//打开文件int     (*close)(struct file *filep);	//关闭文件ssize_t (*read)(struct file *filep, char *buffer, size_t buflen);	//读文件ssize_t (*write)(struct file *filep, const char *buffer, size_t buflen);//写文件off_t   (*seek)(struct file *filep, off_t offset, int whence);//寻找,检索 文件int     (*ioctl)(struct file *filep, int cmd, unsigned long arg);//对文件的控制命令int     (*mmap)(struct file* filep, struct VmMapRegion *region);//内存映射实现<文件/设备 - 线性区的映射>/* The two structures need not be common after this point */#ifndef CONFIG_DISABLE_POLLint     (*poll)(struct file *filep, poll_table *fds);	//轮询接口
#endifint     (*stat)(struct file *filep, struct stat* st);	//统计接口int     (*fallocate)(struct file* filep, int mode, off_t offset, off_t len);int     (*fallocate64)(struct file *filep, int mode, off64_t offset, off64_t len);int     (*fsync)(struct file *filep);ssize_t (*readpage)(struct file *filep, char *buffer, size_t buflen);int     (*unlink)(struct Vnode *vnode);
};

PathCache | 路径缓存

PathCache是路径缓存,它通过哈希表存储,利用父节点Vnode的地址和子节点的文件名,可以从PathCache中快速查找到子节点对应的Vnode。当前PageCache仅支持缓存二进制文件,在初次访问文件时通过mmap映射到内存中,下次再访问时,直接从PageCache中读取,可以提升对同一个文件的读写速度。另外基于PageCache可实现以文件为基底的进程间通信。下图展示了文件/目录的查找流程。

LIST_HEAD g_pathCacheHashEntrys[LOSCFG_MAX_PATH_CACHE_SIZE];	//路径缓存哈希表项
struct PathCache {//路径缓存struct Vnode *parentVnode;    /* vnode points to the cache */	struct Vnode *childVnode;     /* vnode the cache points to */LIST_ENTRY parentEntry;       /* list entry for cache list in the parent vnode */LIST_ENTRY childEntry;        /* list entry for cache list in the child vnode */LIST_ENTRY hashEntry;         /* list entry for buckets in the hash table */uint8_t nameLen;              /* length of path component */
#ifdef LOSCFG_DEBUG_VERSIONint hit;                      /* cache hit count*/
#endifchar name[0];                 /* path component name */
};
//路径缓存初始化
int PathCacheInit(void)
{for (int i = 0; i < LOSCFG_MAX_PATH_CACHE_SIZE; i++) {LOS_ListInit(&g_pathCacheHashEntrys[i]);}return LOS_OK;
}

挂载点管理

当前OpenHarmony内核中,对系统中所有挂载点通过链表进行统一管理。挂载点结构体中,记录了该挂载分区内的所有Vnode。当分区卸载时,会释放分区内的所有Vnode。

static LIST_HEAD *g_mountList = NULL;//挂载链表,上面挂的是系统所有挂载点
struct Mount {LIST_ENTRY mountList;              /* mount list */			 //通过本节点将Mount挂到全局Mount链表上const struct MountOps *ops;        /* operations of mount */ //挂载操作函数	struct Vnode *vnodeBeCovered;      /* vnode we mounted on */ //要被挂载的节点 即 /bin1/vs/sd 对应的 vnode节点struct Vnode *vnodeCovered;        /* syncer vnode */		 //要挂载的节点	即/dev/mmcblk0p0 对应的 vnode节点struct Vnode *vnodeDev;            /* dev vnode */LIST_HEAD vnodeList;               /* list of vnodes */		//链表表头int vnodeSize;                     /* size of vnode list */	//节点数量LIST_HEAD activeVnodeList;         /* list of active vnodes */	//激活的节点链表int activeVnodeSize;               /* szie of active vnodes list *///激活的节点数量void *data;                        /* private data */	//私有数据,可使用这个成员作为一个指向它们自己内部数据的指针uint32_t hashseed;                 /* Random seed for vfs hash */ //vfs 哈希随机种子unsigned long mountFlags;          /* Flags for mount */	//挂载标签char pathName[PATH_MAX];           /* path name of mount point */	//挂载点路径名称  /bin1/vs/sdchar devName[PATH_MAX];            /* path name of dev point */		//设备名称 /dev/mmcblk0p0
};
//分配一个挂载点
struct Mount* MountAlloc(struct Vnode* vnodeBeCovered, struct MountOps* fsop)
{struct Mount* mnt = (struct Mount*)zalloc(sizeof(struct Mount));//申请一个mount结构体内存,小内存分配用 zallocif (mnt == NULL) {PRINT_ERR("MountAlloc failed no memory!\n");return NULL;}LOS_ListInit(&mnt->activeVnodeList);//初始化激活索引节点链表LOS_ListInit(&mnt->vnodeList);//初始化索引节点链表mnt->vnodeBeCovered = vnodeBeCovered;//设备将装载到vnodeBeCovered节点上vnodeBeCovered->newMount = mnt;//该节点不再是虚拟节点,而作为 设备结点
#ifdef LOSCFG_DRIVERS_RANDOM	//随机值	驱动模块HiRandomHwInit();//随机值初始化(VOID)HiRandomHwGetInteger(&mnt->hashseed);//用于生成哈希种子HiRandomHwDeinit();//随机值反初始化
#elsemnt->hashseed = (uint32_t)random(); //随机生成哈子种子
#endifreturn mnt;
}

fd管理 | 两种描述符/句柄的关系

Fd(File Descriptor)是描述一个打开的文件/目录的描述符。当前OpenHarmony内核中,fd总规格为896,分为三种类型:

  • 普通文件描述符,系统总数量为512。
    #define CONFIG_NFILE_DESCRIPTORS    512	// 系统文件描述符数量
  • Socket描述符,系统总规格为128。
    #define LWIP_CONFIG_NUM_SOCKETS         128	//socket链接数量#define CONFIG_NSOCKET_DESCRIPTORS  LWIP_CONFIG_NUM_SOCKETS 
  • 消息队列描述符,系统总规格为256。
    #define CONFIG_NQUEUE_DESCRIPTORS    256

请记住,在OpenHarmony内核中,在不同的层面会有两种文件句柄::

  • 系统文件描述符(sysfd),由内核统一管理,和进程描述符形成映射关系,一个sysfd可以被多个profd映射,也就是说打开一个文件只会占用一个sysfd,但可以占用多个profd,即一个文件被多个进程打开.

  • 进程文件描述符(profd),由进程管理的叫进程文件描述符,内核对不同进程中的fd进行隔离,即进程只能访问本进程的fd.举例说明之间的关系:

    文件            sysfd     profd吃个桃桃.mp4        10    13(A进程)吃个桃桃.mp4        10    3(B进程)容嬷嬷被冤枉.txt    12    3(A进程)容嬷嬷被冤枉.txt    12    3(C进程)
  • 不同进程的相同fd往往指向不同的文件,但有三个fd例外

    • STDIN_FILENO(fd = 0) 标准输入 接收键盘的输入
    • STDOUT_FILENO(fd = 1) 标准输出 向屏幕输出
    • STDERR_FILENO(fd = 2) 标准错误 向屏幕输出
      sysfd和所有的profd的(0,1,2)号都是它们.熟知的 printf 就是向 STDOUT_FILENO中写入数据.
  • 具体涉及结构体

    struct file_table_s {//进程fd <--> 系统FD绑定intptr_t sysFd; /* system fd associate with the tg_filelist index */};//sysFd的默认值是-1struct fd_table_s {//进程fd表结构体unsigned int max_fds;//进程的文件描述符最多有256个struct file_table_s *ft_fds; /* process fd array associate with system fd *///系统分配给进程的FD数组 ,fd 默认是 -1fd_set *proc_fds;	//进程fd管理位,用bitmap管理FD使用情况,默认打开了 0,1,2	       (stdin,stdout,stderr)fd_set *cloexec_fds;sem_t ft_sem; /* manage access to the file table */ //管理对文件表的访问的信号量};struct files_struct {//进程文件表结构体int count;				      //持有的文件数量struct fd_table_s *fdt; //持有的文件表unsigned int file_lock;	//文件互斥锁unsigned int next_fd;	  //下一个fd#ifdef VFS_USING_WORKDIRspinlock_t workdir_lock;	//工作区目录自旋锁char workdir[PATH_MAX];		//工作区路径,最大 256个字符#endif};typedef struct ProcessCB {#ifdef LOSCFG_FS_VFSstruct files_struct *files;        /**< Files held by the process */ //进程所持有的所有文件,注者称之为进程的文件管理器#endif	//每个进程都有属于自己的文件管理器,记录对文件的操作. 注意:一个文件可以被多个进程操作}

解读

  • 鸿蒙的每个进程ProcessCB都有属于自己的进程的文件描述符files_struct,该进程和文件系统有关的信息都由它表达.
  • 搞清楚 files_struct,fd_table_s,file_table_s三个结构体的关系就明白了进度描述符和系统描述符的关系.
  • fd_table_s是由alloc_fd_table分配的一个结构体数组,用于存放进程的文件描述符
        //分配进程文件表,初始化 fd_table_s 结构体中每个数据,包括系统FD(0,1,2)的绑定static struct fd_table_s * alloc_fd_table(unsigned int numbers){struct fd_table_s *fdt;void *data;fdt = LOS_MemAlloc(m_aucSysMem0, sizeof(struct fd_table_s));//申请内存if (!fdt){goto out;}fdt->max_fds = numbers;//最大数量if (!numbers){fdt->ft_fds = NULL;fdt->proc_fds = NULL;return fdt;}data = LOS_MemAlloc(m_aucSysMem0, numbers * sizeof(struct file_table_s));//这是和系统描述符的绑定if (!data){goto out_fdt;}fdt->ft_fds = data;//这其实是个 int[] 数组,for (int i = STDERR_FILENO + 1; i < numbers; i++){fdt->ft_fds[i].sysFd = -1;//默认的系统描述符都为-1,即还没有和任何系统文件描述符绑定}data = LOS_MemAlloc(m_aucSysMem0, sizeof(fd_set));//管理FD的 bitmap if (!data){goto out_arr;}(VOID)memset_s(data, sizeof(fd_set), 0, sizeof(fd_set));fdt->proc_fds = data;alloc_std_fd(fdt);//分配标准的0,1,2系统文件描述符,这样做的结果是任务进程都可以写系统文件(0,1,2)(void)sem_init(&fdt->ft_sem, 0, 1);//互斥量初始化return fdt;out_arr:(VOID)LOS_MemFree(m_aucSysMem0, fdt->ft_fds);out_fdt:(VOID)LOS_MemFree(m_aucSysMem0, fdt);out:return NULL;}
  • file_table_s记录 sysfdprofd的绑定关系.fdt->ft_fds[i].sysFd中的i就是profd

鸿蒙全栈开发全新学习指南

也为了积极培养鸿蒙生态人才,让大家都能学习到鸿蒙开发>鸿蒙开发最新的技术,针对一些在职人员、0基础小白、应届生/计算机专业、鸿蒙爱好者等人群,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线【包含了大厂APP实战项目开发】

本路线共分为四个阶段:

第一阶段:鸿蒙初中级开发必备技能

第二阶段:鸿蒙南北双向高工技能基础:gitee.com/MNxiaona/733GH

第三阶段:应用开发中高级就业技术

第四阶段:全网首发-工业级南向设备开发就业技术:https://gitee.com/MNxiaona/733GH

鸿蒙 (Harmony OS)开发学习手册》(共计892页)

如何快速入门?

1.基本概念
2.构建第一个ArkTS应用
3.……

开发基础知识:gitee.com/MNxiaona/733GH

1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……

基于ArkTS 开发

1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……

鸿蒙开发>鸿蒙开发面试真题(含参考答案):gitee.com/MNxiaona/733GH

鸿蒙入门教学视频:

美团APP实战开发教学:gitee.com/MNxiaona/733GH

写在最后

  • 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
  • 想要获取更多完整鸿蒙最新学习资源,请移步前往小编:gitee.com/MNxiaona/733GH


http://www.ppmy.cn/news/1461496.html

相关文章

HTML常用标签-表单标签

表单标签 1 表单标签2 表单项标签2.1 单行文本框2.2 密码框2.3 单选框2.4 复选框2.5 下拉框2.6 按钮2.7 隐藏域2.8 多行文本框2.9 文件标签 1 表单标签 表单标签,可以实现让用户在界面上输入各种信息并提交的一种标签. 是向服务端发送数据主要的方式之一 form标签,表单标签,其内…

MySQL用SQL取三列中最大的数据值

1、有如下数据&#xff1a; ABC000097.0600330.72330.720069.650027.8827.85086.92086.92219.42219.4219.41 需要展示为如下形式&#xff1a; ABC结果列0000097.06097.060330.72330.72330.7200669.65009.6527.8827.85027.8886.92086.9286.92219.42219.4219.41219.42 解决办…

git仓库使用

git仓库是会限制空间大小限制的 git网络库的容量限制_github仓库大小限制-CSDN博客 git是用于管理github的工具 电脑左下角搜索git打开GitBash.exe 进入到要下载到本地的目录 下载到本地的文件不要更改&#xff01; 如果要使用请务必把文件复制到别的空间去再在这个别的空间…

Linux基础知识面试题

1. 请描述Linux操作系统的安装过程&#xff0c;并说明其中的关键步骤。 Linux操作系统的安装过程通常涉及以下几个关键步骤&#xff1a; 准备安装介质&#xff1a;需要从官网或者其他可靠来源下载Linux发行版的ISO镜像文件&#xff0c;并制作一个启动U盘或者烧录到DVD中。现在…

C++自定义脚本文件执行

FunctionCall.h&#xff1a; #include <sstream> #include <string> #include <vector> // 函数调用 class FunctionCall { public: FunctionCall(); ~FunctionCall(); std::string call(const st…

Kotlin协程中调度器Dispatchers的介绍

关于Kotlin中协程使用的调度器&#xff08;Dispatchers&#xff09;的介绍 viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) {// todo }在 Kotlin 的协程中&#xff0c;Dispatchers 是用于指定协程运行的调度器&#xff08;dispatcher&#xff09;&#xff0c;它决…

Jira Server 不维护了,如何将 Jira 平滑迁移到阿里云云效

作者&#xff1a;天彤 Atlassian 在 2020 年官方发布公告&#xff0c;从 2021 年起停止 Jira Server 产品的销售&#xff0c;并且在 2024 年彻底停止 Server 端产品的服务支持&#xff0c;这对于国内使用 Jira 产品的企业和研发团队造成了不小的影响。而此时国内很多 DevOps 产…

分布式链路追踪 Zipkin+Sleuth(8)

项目的源码地址 Spring Cloud Alibaba 工程搭建&#xff08;1&#xff09; Spring Cloud Alibaba 工程搭建连接数据库&#xff08;2&#xff09; Spring Cloud Alibaba 集成 nacos 以及整合 Ribbon 与 Feign 实现负载调用&#xff08;3&#xff09; Spring Cloud Alibaba Ribbo…

C++:位图和布隆过滤器

一&#xff0c;位图 1.1 位图的概念 究竟什么是位图呢&#xff1f;&#xff1f;我们用一道问题来引入 问题&#xff1a;给40亿个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&#xff0c;如何快速判断一个数是否在 这40亿个数中。【腾讯】 根据这个问题&#x…

Flutter 中的 ListTile 小部件:全面指南

Flutter 中的 ListTile 小部件&#xff1a;全面指南 在Flutter中&#xff0c;ListTile是一个用于快速创建列表项的组件&#xff0c;它通常用于ListView中&#xff0c;以展示包含文本、图标、开关、滑块等元素的行。ListTile不仅使得界面看起来美观&#xff0c;而且提供了一种简…

C++ | Leetcode C++题解之第90题子集II

题目&#xff1a; 题解&#xff1a; class Solution { public:vector<int> t;vector<vector<int>> ans;vector<vector<int>> subsetsWithDup(vector<int> &nums) {sort(nums.begin(), nums.end());int n nums.size();for (int mask …

计算机毕业设计springboot体育馆场地预约管理系统【附源码】

计算机毕业设计springboot体育馆场地预约管理系统[附源码] &#x1f345; 作者主页 网顺技术团队 &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; &#x1f345; 文末获取源码联系方式 &#x1f4dd; &#x1f345; 查看下方微信号获取联系方式 承接各种定制…

C# winform opencvsharp 识别轮廓。拟合,找到中心点

在C# WinForms项目中使用OpenCvSharp来识别图像中的轮廓并对其进行拟合以找到中心点&#xff0c;你可以遵循以下步骤&#xff1a; 准备工作 安装OpenCvSharp&#xff1a;确保你的项目中已经通过NuGet包管理器安装了OpenCvSharp4&#xff08;或相应的版本&#xff09;。 导入命…

【scikit-learn001】逻辑回归(Logistic Regression)ML模型实战及经验总结(更新中)

1.一直以来想写下基于scikit-learn训练AI算法的系列文章&#xff0c;作为较火的机器学习框架&#xff0c;也是日常项目开发中常用的一款工具&#xff0c;最近刚好挤时间梳理、总结下这块儿的知识体系。 2.熟悉、梳理、总结下scikit-learn框架逻辑回归&#xff08;Logistic Regr…

海豚调度器如何看工作流是在哪个worker节点执行

用海豚调度器&#xff0c;执行一个工作流时&#xff0c;有时成功&#xff0c;有时失败&#xff0c;怀疑跟worker节点环境配置不一样有关。要怎样看是在哪个worker节点执行&#xff0c;在 海豚调度器 Web UI 中&#xff0c;您可以查看任务实例&#xff0c;里面有一列显示host&a…

队列的讲解

队列的概念 队列:只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表&#xff0c;队列具有先进先出FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾 出队列:进行删除操作的一端称为队头 一端进另一端出 也就是可以做到&#xff0c;先…

windows下mysql5.6下载安装(多实例安装)

目录 1、下载 2、安装步骤 3、mysql多实例安装 1、下载 http://dev.mysql.com/downloads/windows/installer/5.6.html 2、安装步骤 我们采用自定义安装模式&#xff1a;选择32位或64位 默认即可&#xff1a; 说明&#xff0c;如果没有该页面就上一步&#xff0c;选择高级选…

Android ndk获取手机内部存储卡的根目录方法

如下所示&#xff1a; 代码语言&#xff1a;javascript jclass envcls env- FindClass("android/os/Environment"); //获得类引用if (envcls nullptr) return 0;//找到对应的类&#xff0c;该类是静态的返回值是FilejmethodID id env- GetStaticMethodID(envcls,…

VueRouter使用总结

VueRouter 是 Vue.js 的官方路由管理器&#xff0c;用于构建单页面应用&#xff08;SPA&#xff09;。在使用 VueRouter 时&#xff0c;开发者可以定义路由映射规则&#xff0c;并在 Vue 组件中通过编程式导航或声明式导航的方式控制页面的跳转和展示。以下是 VueRouter 使用的…

C语言中的控制语句:深入解析与案例实践3

11. 动态内存管理 在C语言中&#xff0c;我们可以使用动态内存管理函数来在运行时分配和释放内存。这为我们提供了更大的灵活性&#xff0c;特别是在处理大小可变的数据时。 #include <stdio.h> #include <stdlib.h> int main() {int n, *p, i;printf("Enter…