linux - 基础IO之操作与文件描述符全解析:从C语言到系统调用底层实现

目录

1.回顾c语言中所学的文件

2.提炼对文件的理解(linux基础io第一阶段的学习)

a.在操作系统内部,一个进程和一个被打开的文件,他们到后面会变成两种对象之间的指针关系。

b.文件 = 属性 + 内容

c.在c语言中,以w的方式打开文件,默认打开文件的时候,就会先把目标文件给清空。a则是追加

d.输出重定向  ‘  > ’--->先清空再写入(以w方式打开文件),追加重定向 '>>'具体可以看博客linux-基本指令与用法02(超详细!!)_linux的02-CSDN博客

3.理解文件

open()系统调用函数

参数说明

返回值

打开现有文件(只读)

  独占创建文件(避免竞态条件)

创建新文件(读写权限,若存在则清空)

设计传递位图标记位的函数

close()系统调用函数

close() 的作用

close()参数:

write()系统调用函数

参数说明:

返回值:

write() 与 fwrite() 对比

什么叫做fd(文件描述符):

为什么不返回 0、1、2?

fd的本质是什么?

fd的本质是:内核的进程:文件映射关系的数组的下标

一切皆文件 

c语言上的文件操作函数,本质底层都是对系统调用的封装

c语言为什么要这么做:


1.回顾c语言中所学的文件

进行文件操作,前提是我们的程序在执行了,所谓的文件的打开和关闭,是cpu在执行代码,比如执行fopen(),才将文件打开的。

当一个代码执行的时候已经变成了一个进程,所以在建立文件时,他就会默认的接在当前进程所处路径后,拼上所创建的文件名,创建的这个文件。

(进程启动时所处的路径:当前进程的当前工作路径。)

2.提炼对文件的理解(linux基础io第一阶段的学习)

a.在操作系统内部,一个进程和一个被打开的文件,他们到后面会变成两种对象之间的指针关系。

  • 打开文件,本质是进程打开文件。
  • 当文件本就存在时,文件没有被打开的时候,存在磁盘上
  • 一个进程可以打开多个文件。
  • 系统当中可以存在多个进程,大多数情况下,OS内部,一定存在大量被打开的文件,每个进程可能都打开很多文件。
  • 文件未被打开时处于磁盘上,处于一个硬件上,因此只能被操作系统(操作系统是硬件的管理者)打开(用的是c语言接口),因此操作系统会对这些被打开的文件进行管理(先描述,再组织)。
  • 每一个被打开的文件,在os内部,一定会存在对应的描述文件属性的结构体,类似于PCB。

b.文件 = 属性 + 内容

当我在磁盘上新建一个文本文件,但并不打开,并不往其中填写任何数据,他在磁盘上所占据的大小是0KB,此时他是否会占据磁盘空间?会占据,文件的名字、文件建立的时间、文件的大小等等文件的属性就已经占据了磁盘空间了。0kb指的是内容为0。结构体放的就是文件的属性。

1.打开文件 2.并向文件 写入3.再关闭。

 

1.打开文件  --- w ,不存在就在当前路径下创建指定文件。 2.并向文件 写入3.再关闭。都是进程让cpu在执行自己的代码。通过这样的方式发开文件,访问文件。

c.在c语言中,以w的方式打开文件,默认打开文件的时候,就会先把目标文件给清空。a则是追加

只以写的方式打开文件:

延续上面的操作本来log.txt中是有内容的,现在被清空了

d.输出重定向  ‘  > ’--->先清空再写入(以w方式打开文件),追加重定向 '>>'具体可以看博客linux-基本指令与用法02(超详细!!)_linux的02-CSDN博客

3.理解文件

a,操作文件、本质:进程在操作文件。进程和文件的关系

b.文件 --> 磁盘(外设)---> 硬件  ----> 向文件中写入,本质是向硬件中写入。 ---->  用户没有权利直接向硬件写入  ---->  硬件的管理者是操作系统 ------>  用户无法绕过操作系统去处理硬件(嵌入式除外)----> 用户必须通过OS来写入 ---->  操作系统给用户提供系统调用 ----> c语言 / c++ ...都是对系统调用接口的封装   ---->  访问文件,就可以用系统调用

c++写入文件:

#include<iostream>
#include<string>
#include<fstream>#define FILENAME "log.txt"using namespace std;int main()
{std::ofstream out(FILENAME, std::ios::binary);if(!out.is_open()) return 1;string message("hello c++\n");out.write(message.c_str(), message.size());out.close();return 0;
}

open()系统调用函数

       #include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>int open(const char *pathname, int flags);int open(const char *pathname, int flags, mode_t mode);

参数说明

pathname

类型:const char*

作用:要打开或创建的文件路径(绝对或相对路径)。

flags

类型:int

作用:指定文件的打开方式,多个标志可通过按位或(|)组合。

常用标志:

必选标志(三选一):

O_RDONLY: 只读模式、O_WRONLY: 只写模式、O_RDWR: 读写模式。

可选标志:(O代表open的意思)

O_CREAT:

        文件不存在时创建新文件,需配合mode参数设置权限。

O_TRUNC:

        若文件存在且为普通文件,将其长度截断为0。

O_APPEND:

        追加写入(每次写操作前移动到文件末尾)。

O_EXCL:

        O_CREAT联用时,若文件已存在则返回错误(用于原子性创建文件)。

O_NONBLOCK:

         非阻塞模式(对设备文件或管道有效)。

mode

类型:mode_t

作用:创建文件时的权限(仅当使用O_CREAT时需指定)。

常见值(八进制表示):

0644: 用户可读写,组和其他用户只读。

0755: 用户可读写执行,组和其他用户可读执行。

注意:实际权限为 mode & ~umaskumask用于过滤权限位)。

返回值

  • 成功:返回文件描述符(非负整数),用于后续操作(如readwrite)。

  • 失败:返回-1,并设置errno指示错误类型(如ENOENT文件不存在、EACCES权限不足)

打开现有文件(只读)

当已经存在文件时,可以不传权限的参数

#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>int main() {int fd = open("test.txt", O_RDONLY);if (fd == -1) {perror("open failed");return 1;}close(fd);return 0;
}

  独占创建文件(避免竞态条件)

int fd = open("unique.txt", O_WRONLY | O_CREAT | O_EXCL, 0644);
if (fd == -1) {perror("file already exists");return 1;
}

创建新文件(读写权限,若存在则清空)

创建新文件时,一定要传权限码,例如:0666:用户、组、其他,默认都是可读可写的权限

-rwxr-xr--  1 user  group  4096 Jun 1 10:00 file.txt
▲ ▲▲▲ ▲▲▲ ▲▲▲ 
│ │││ │││ │││ 
│ └──────┬─────── 权限(用户u、组g、其他o)
└───────── 文件类型(`-`普通文件,`d`目录)

八进制:

 数字表示(八进制):

  • r=4, w=2, x=1,三者相加:

    • rwxr-xr-- → 7(4+2+1) 5(4+0+1) 4(4+0+0) → 权限数字 754

int fd = open("log.txt", O_WRONLY | O_CREAT, 0666);
if (fd == -1) {perror("open");return 1;
}

没有传权限码就会出现权限处乱码:

传了0666:

这是因为:最终权限 = 默认最大权限 - umask 值(实际是位运算 默认权限 & ~umask

如何一次性创建好需求权限的文件:

umask()系统调用

 #include <sys/types.h>#include <sys/stat.h>mode_t umask(mode_t mask);

修改原代码:将系统掩码设置为0,没设置的时候就用系统默认的

  umask(0);int fd = open("log.txt", O_WRONLY | O_CREAT, 0666);if(fd < 0){perror("open");return 1;}

输出:

补充:文件权限计算

  • 默认最大权限:666(二进制 110 110 110

  • umask 值:002(二进制 000 000 010

  • 实际权限:666 - 002 = 664

    (即 rw-rw-r--,用户和组可读写,其他只读)

将这个整数参数看作是一张位图

设计传递位图标记位的函数

通过设计一个传递位图标记位的函数来理解os 设计很多系统调用接口的方法:

#include <stdio.h> 
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define ONE    1     // 1 0000 0001
#define TWO   (1<<1) // 2 0000 0010
#define THREE (1<<2) // 4 0000 0100
#define FOUR  (1<<3) // 8 0000 1000void print(int flag)
{if(flag & ONE)printf("one\n");if(flag & TWO)printf("two\n");if(flag & THREE)printf("three\n");if(flag & FOUR)printf("four\n");
}int main()
{print(ONE);printf("\n");print(TWO);printf("\n");print(ONE | TWO);printf("\n");print(ONE | TWO | THREE);printf("\n");print(ONE | FOUR);printf("\n");print(ONE | TWO | THREE | FOUR);printf("\n");return 0;
}

通过标志位来让我们实现对应的功能,向指定的函数传递多种标记位的方法,标记位传参

close()系统调用函数

close() 的作用

释放资源

  • 每个打开的文件描述符都会占用系统资源(如内核中的文件表项、缓冲区等)。
  • 调用 close() 后,系统会释放这些资源。

刷新缓冲区

  • 如果文件是以写入模式打开的,close() 会确保所有缓冲区的数据写入磁盘(类似于 fflush())。

解除文件描述符的绑定

  • 关闭后,文件描述符不再与任何文件或资源关联,可以被重新用于其他文件。

避免资源泄漏

  • 如果不关闭文件描述符,可能会导致文件描述符耗尽(每个进程有文件描述符数量限制)。

close()参数:

#include <unistd.h>int close(int fd);
  • fd:要关闭的文件描述符(通常由 open()socket() 等函数返回)。

返回值:

  • 成功时返回 0

  • 失败时返回 -1,并设置 errno 表示错误原因。

write()系统调用函数

一、函数原型与头文件

#include <unistd.h>ssize_t write(int fd, const void *buf, size_t count);

参数说明:

fd(文件描述符)

  • 已打开文件的描述符(由 open()socket() 等函数返回)。
  • 必须具有 可写权限(例如以 O_WRONLY 或 O_RDWR 模式打开)。

buf(数据缓冲区)

  • 指向用户空间缓冲区的指针,包含待写入的数据。
  • 可以是任意类型的数据(如字符串、二进制数据)。

count(写入字节数)

  • 指定从 buf 中写入的字节数。
  • 实际写入的字节数可能小于 count(需检查返回值)。

返回值:

成功时:返回实际写入的字节数(0 ≤ 返回值 ≤ count)。

  • 返回值为 0 表示未写入数据(例如写入到已满的管道)。
  • 返回值小于 count 表示部分写入(需处理剩余数据)。

失败时:

  • 返回 -1,并设置全局变量 errno 表示错误类型

write() 与 fwrite() 对比

特性write()fwrite()
接口层级系统调用(底层)标准库函数(高层)
缓冲无缓冲(直接写入内核)带用户空间缓冲区
错误处理通过 errno 和返回值通过返回值与 ferror()
适用场景需要精细控制的场景(如非阻塞)常规文件操作(更便捷)

什么叫做fd(文件描述符):

strlen 函数:只用写入有效字符串

输出:

很显然第二次的写入是在上一次的基础上从头开始写的。

以写的方式打开,不存在就创建,并且先清空文件内容

int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);

open()的返回值fd是什么?这里我创建了四个文件并记录他们的返回值

输出结果:

为什么不返回 0、1、2?

因为:                                                                                                                                                                                        

  • 1:标准输入 - 键盘
  • 2:标准输出 - 显示器
  • 3:标准错误 - 显示器

这里和c语言的进行对比

stdin、stdout、stderr对应的类型都是文件指针,与c语言的fopen、fdopen、freopen的返回值是一样的

这些说明,在c语言中我们把键盘显示器也是当做文件来看的

也就是说,由于write()是根据open返回的fd,来查找文件并写入的,那我直接往1中写入不就是往显示器文件中写吗:

编译运行:

fd  --->   文件描述符   --->  文件描述符的本质是什么?

fd的本质是什么?

1.在打开文件的时候,会在操作系统中创建一个struct file,文件的内核数据结构所包含的是文件的属性(权限,什么方式被打开,标记位),所有被打开的文件,他们的内核数据结构以双链表的形式被链接起来,操作系统对文件的管理转变成对链表的增删查改,每一个struct file内部都有一个指向与 该文件所对应的文件内核级的缓存的 指针,操作系统给文件申请的内存。

一个磁盘上的文件,会经过属性struct file内核数据结构 初始化,内容直接存到这个文件的缓存当中。未来直接从缓存当中读写修改。

os中有多个进程,每个进程都有可能打开多个文件,进程和文件的关系是1:n,进程的内核数据结构中存在一个struct files_struct *file属性。os中还会存在一个struct file_struct内核数据结构,整个结构中会包含一个指针数组struct file* fd_array[N]。

想让进程和对应的文件产生关系:

将描述文件的结构体变量的地址(文件属性的地址),依次填入到 fd_array[],特定的数组中,因此一个进程想要找到对应的文件,只需要把对应文件数组的下标返回给上层比如说 int fd,就可以访问文件了。每个进程都有一个指针*files, 指向一张表files_struct,该表最重要的部分就是包涵一个指针数组,每个元素都是一个指向打开文件的指针!所以,本质上,文件描述符就是该数组的下标。所以,只要拿着文件描述符,就可以找到对应的文件

fd的本质是:内核的进程:文件映射关系的数组的下标

读的本质是:将文件缓冲区的内容拷贝到需要的文件当中去,如果对应的内容不在缓冲区里,os就会把这个打开文件的进程阻塞住,挂起,os再将磁盘中的数据搬到缓存当中,再唤醒进程。

写和修改内容:没有内容的时候上层就直接将内容拷贝到缓冲区,当文件中本来就有内容的时候,现将内容拷贝到缓冲区,再在内存当中修改,定期由os再刷新

1.无论读写,都必须让os把内容读到对应的文件缓冲区内,在内存中修改,再刷新到磁盘

2.open()在:

a.创建file

b.开辟文件缓冲区的空间,加载文件数据(延后)

c.查进程的文件描述符表

d.将file地址,填入对应的表中

e.返回下标

3.write()、read()函数的本质是拷贝函数

一切皆文件 

像是键盘、鼠标、显示器、网卡、磁盘这些外设,他们可以由一个设备结构体来记录他们的属性,但是,他们每一个的操作方法都不同,这是通过驱动来控制的。对每一个设备os都会构建一个struct file,里面就会包含他们的读写的函数指针,再指向驱动层的方法。使用同一个类,其中包含的读写函数指针指向不同的设备,因此我们就不用再管底层的差异了,因为底层外设的方法--->归于函数指针  ---》一切皆文件   

这就像c++中的多态

这是一个指针指向一张操作表:

每一个被打开的文件还会有一张,操作底层方法的指针表

在操作系统中,这就叫做虚拟文件系统:virtual file system

在操作系统中,系统访问文件时只认文件描述符fd

如何理解c语言通过FILE* 访问文件? 这个FILE是一个结构体

因此这个FILE里面一定封装了fd文件描述符

c语言上的文件操作函数,本质底层都是对系统调用的封装

写如下代码:

  FILE* fp = fopen("log.txt", "w");if(fp == NULL){perror("fopen");return 1;}printf("fd: %d\n",fp->_fileno);fwrite("hello\n", 5, 1, fp);fclose(fp);

运行结果:这就证明了我们的FILE结构体中封装了fd文件描述符,

更进一步:

代码:

  FILE* fp1 = fopen("log1.txt", "w");if(fp1 == NULL){perror("fopen");return 1;}printf("fd1: %d\n",fp1->_fileno);FILE* fp2 = fopen("log2.txt", "w");if(fp2 == NULL){perror("fopen");return 1;}printf("fd2: %d\n",fp2->_fileno);FILE* fp3 = fopen("log3.txt", "w");if(fp3 == NULL){perror("fopen");return 1;}printf("fd3: %d\n",fp3->_fileno);fclose(fp1);fclose(fp2);fclose(fp3);

输出结果:

c语言的stdout:stdin:stderr:

printf("stdin ->fd: %d\n", stdin->_fileno);printf("stdout->fd: %d\n", stdout->_fileno);printf("stderr->fd: %d\n", stderr->_fileno);

最后输出

c语言为什么要这么做:

本来可以使用系统调用,也可以使用语言提供的文件方法

系统不一样,系统调用接口就不一样,代码不具有跨平台性,而为什么c语言、c++....等所有的语言都具有跨平台性的原因和作用我们现在就知道了。

如图:

所有的语言要对不同的平台的系统调用进行封装,不同语言封装时,文件接口就有差别了

在c++中的cin、cout、cerr可以向文件,显示器都写,我们称他们为流,但cin、cout、cerr在c++中都叫做类,他们内部一定包含了文件描述符。

通过进程pid找到fd

终端文件:

这个终端也是属于一个文件,因此实际上我也可以向这个终端直接写东西:

运行结果:

结语:

       随着这篇关于题目解析的博客接近尾声,我衷心希望我所分享的内容能为你带来一些启发和帮助。学习和理解的过程往往充满挑战,但正是这些挑战让我们不断成长和进步。我在准备这篇文章时,也深刻体会到了学习与分享的乐趣。    

         在此,我要特别感谢每一位阅读到这里的你。是你的关注和支持,给予了我持续写作和分享的动力。我深知,无论我在某个领域有多少见解,都离不开大家的鼓励与指正。因此,如果你在阅读过程中有任何疑问、建议或是发现了文章中的不足之处,都欢迎你慷慨赐教。               

        你的每一条反馈都是我前进路上的宝贵财富。同时,我也非常期待能够得到你的点赞、收藏,关注这将是对我莫大的支持和鼓励。当然,我更期待的是能够持续为你带来有价值的内容,让我们在知识的道路上共同前行。

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

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

相关文章

【A2DP】深入解读A2DP中通用访问配置文件(GAP)的互操作性要求

目录 一、模式支持要求 1.1 发现模式 1.2 连接模式 1.3 绑定模式 1.4 模式间依赖关系总结 1.5 注意事项 1.6 协议设计深层逻辑 二、安全机制&#xff08;Security Aspects&#xff09; 三、空闲模式操作&#xff08;Idle Mode Procedures&#xff09; 3.1 支持要求 …

python 入门教程 window 10 环境下安装pyenv

python的环境配置方法很多&#xff0c;由于python有两个大版本&#xff0c;很多时候需要切换某个固定的版本才能运行三方包&#xff0c;所以推荐使用pyenv 配置python 环境变量 pyenv 的安装 安装方法&#xff1a; Invoke-WebRequest -UseBasicParsing -Uri "https://r…

【fNIRS可视化学习1】基于NIRS-SPM进行光极可视化并计算通道坐标

一、前言 功能性近红外光谱(fNIRS)是一种无创的脑功能成像技术。在fNIRS研究中&#xff0c;光极的空间定位和通道坐标的计算至关重要。 1.光极可视化 光极可视化的重要性我就不赘述了&#xff0c;它可以直观检查probe设计的合理性&#xff0c;确认光极覆盖目标脑区&#xff0c…

Vue.js 中 class 和 style 绑定的全面解析

目录 引言 6.1 v-bind 指令 介绍 使用方法 6.2 绑定 HTML class 介绍 用法 6.3 绑定内联样式 介绍 用法 6.4 实战&#xff1a;制作消息提示框 介绍 用法 总结 引言 在Vue.js构建用户界面的宏伟蓝图里&#xff0c;样式的动态呈现与交互性的完美融合是吸引用户目光…

【红黑树】—— 我与C++的不解之缘(二十五)

前言 学习了avl树&#xff0c;现在来学习红黑树。 一、什么是红黑树 红黑树是一颗平衡二叉搜索树&#xff0c;它每一个节点增加了一个存储位表示节点的颜色&#xff0c;可以是红色或者黑色。 相比较于AVL树&#xff0c;红黑树也是一个自平衡二叉搜索树&#xff0c;但是它与AVL树…

SFT数据处理部分的思考

SFT数据及处理的业内共识 1&#xff0e;prompt的质量和多样性远重要于数据量级&#xff0c;微调一个 30 b 量级的base model只需要 10 w 量级的数据即可 参考&#xff1a;《LIMA&#xff1a;Less Is More for Alignment》 2&#xff0e;合成数据很重要&#xff01;一般需要通过…

Python(学习一)

做网站有成熟的框架像FLASK、DJANGO、TORNADO&#xff0c;写爬虫有好用到哭的REQUESTS&#xff0c;还有强大到没盆友的SCRAPY 随着NUMPY、SCIPY、MATLOTLIB等众多第三方模块的开发和完善&#xff0c;不仅支持py支持各种数学运算&#xff0c;还可以绘制高质量的2D和3D图像&…

ArcGIS Pro将有文字标注底图切换为无标注底图(在线地图图源)

今天介绍一下在ArcGIS Pro将有标注的地形底图换成无标注的底图。 大家在这项目底图时候会经常调用ArcGIS Pro自带的地形图&#xff0c;但是这个地形图自带是有注记的&#xff0c;如下图。 如何更改&#xff0c;才可以调用无文字注记的呢&#xff1f; 对于一个已经切好图的有注记…

Linux第三次练习

1、创建根目录结构中的所有的普通文件 首先在根目录下面新创建一个test目录&#xff0c;然后将查找到的普通文件新建到test目录下 2、列出所有账号的账号名 3、将/etc/passwd中内容按照冒号隔开的第三个字符从大到小排序后输出所有内容 4、列出/etc/passwd中的第20行-25行内容…

[CISCN 2022 初赛]ezpop(没成功复现)

打开在线环境可以看到&#xff1a; 记得之前做过一个类似的就是有点像照着漏洞去复现。应该可以直接在网上找到链子去打。 www.zip查看路由是 Index/test&#xff0c;然后 post 传参 a&#xff1a; exp&#xff08;参考了别的大神的wp&#xff09;&#xff1a; <?php //…

技术-NBIOT

是什么&#xff1f; 窄带物联网&#xff08;Narrow Band Internet of Things, NB-IoT&#xff09;成为万物互联网络的一个重要分支支持低功耗设备在广域网的蜂窝数据连接&#xff0c;也被叫作低功耗广域网(LPWAN)NB-IoT支持待机时间长、对网络连接要求较高设备的高效连接NB-Io…

Spring @Bean注解使用场景二

bean:最近在写一篇让Successfactors顾问都能搞明白的sso的逻辑的文章&#xff0c;所以一致在研究IAS的saml2.0的协议&#xff0c;希望用代码去解释SP、idp的一些概念&#xff0c;让顾问了解SSO与saml的关系&#xff0c;在github找代码的时候发现一些代码的调用关系很难理解&…

pip install和conda install的区别

这里写目录标题 一、什么是 Python 依赖&#xff08;Python Dependencies&#xff09;&#xff1f;1. 依赖的作用2. 如何管理 Python 依赖3. 依赖管理问题4. 依赖锁定总结 二、使用pip安装包venv隔离环境方法 1&#xff1a;使用 venv&#xff08;推荐&#xff09;创建虚拟环境激…

R语言高效数据处理-自定义EXCEL数据排版

注&#xff1a;以下代码均为实际数据处理中的笔记摘录&#xff0c;所以很零散 1、自定义excel表数据输出格式、布局 在实际数据处理中为了提升效率&#xff0c;将Excel报表交付给需求方时减少手动调整的环节很有必要 #1.1设置表头格式 header_style <- createStyle(font…

Word 小黑第4套

对应大猫41 上下日期是一起变动的&#xff0c;删掉第一个&#xff0c;第二个日期格式&#xff08;文件 -选项 -自定义功能区 -选上开发工具&#xff09; 点开发工具 -属性 选择相应的日期格式&#xff09; 修改标题样式时&#xff0c;标题三只有点标题二时才会显示 右击正文样…

酒店宾馆IPTV数字电视系统:创新宾客体验,引领智慧服务新潮流

酒店宾馆IPTV数字电视系统&#xff1a;创新宾客体验&#xff0c;引领智慧服务新潮流 北京海特伟业科技有限公司任洪卓于2025年3月15日发布 随着智慧酒店的不断发展&#xff0c;宾客对于酒店内的娱乐和信息服务需求日益多样化&#xff0c;传统的电视服务已难以满足现代宾客的高…

jupyter无法转换为PDF,HTMLnbconvert failed: Pandoc wasn‘t found.

无法转为PDF 手动下载工具 https://github.com/jgm/pandoc/releases/tag/3.6.3 似乎跟我想的不大一样&#xff0c;还有新的报错 https://nbconvert.readthedocs.io/en/latest/install.html#installing-tex 不知道下的啥玩意儿 sudo apt-get install texlive-xetex texlive-fon…

如何在 VS编译器上使用 C99规定的变长数组------使用Clang工具

VS编译器默认处理代码的工具是 MSVC&#xff0c;而MSVC工具是无法处理变长数组的&#xff0c;这个时候我们就要换一个处理代码的工具了----Clang 1 int n 9; 2 int arr[n];// 数组长度可以拟定1.打开 Visual Stdudio Intaller 2.点击修改&#xff0c;鼠标下滑找到>>使用…

vue echarts封装使用

echarts 尺寸自动调节 resize.js 柱状图 components/dashboard/lineChart.vue <template><div :class"className" :style"{height:height,width:width}" /> </template><script> import echarts from echarts require(echarts/…

《计算机图形学》第二课笔记-----二维变换的推导

前言&#xff1a;为什么这么突兀的把这一节内容放在了第二课&#xff0c;第一是因为我急于求成&#xff0c;第二是因为这一章节太重要了&#xff0c;这几乎是二维三维变换的最核心的东西&#xff0c;理解了这一章节内容&#xff0c;后面的就会像打通了任督二脉一样&#xff0c;…