【LInux】<基础IO> 文件操作 | 文件描述符 | 重定向

news2024/9/21 0:25:13

在这里插入图片描述

👦个人主页:Weraphael
✍🏻作者简介:目前正在学习c++和算法
✈️专栏:Linux
🐋 希望大家多多支持,咱一起进步!😁
如果文章有啥瑕疵,希望大佬指点一二
如果文章对你有帮助的话
欢迎 评论💬 点赞👍🏻 收藏 📂 加关注😍


目录

  • 一、C语言文件操作
      • 1.1 打开文件 --- fopen
      • 1.2 关闭文件 --- fclose
      • 1.3 文件写入 --- fwrite
  • 二、文件系统调用
      • 2.1 文件系统调用和函数的关系
      • 2.2 打开文件 --- open
      • 2.3 文件关闭 --- close
      • 2.4 文件写入 --- write
  • 三、文件描述符
      • 3.1 什么是文件描述符
      • 3.2 三个标准流
      • 3.3 文件描述符的分配规则
      • 3.4 引用计数
  • 四、再谈重定向
      • 4.1 输出重定向
      • 4.2 追加重定向
      • 4.3 输入重定向
      • 4.4 系统调用接口 --- dup2
  • 五、补充知识
      • 5.1 stdout和stderr的区别
      • 5.2 为什么Linux下一切皆文件(以源码的角度剖析)
  • 六、相关代码

一、C语言文件操作

1.1 打开文件 — fopen

#include <stdio.h>
FILE* fopen(const char* path, const char* mode);
  • path:要打开文件的相对路径或绝对路径。注意:如果不带路径默认会在当前进程所在路径下创建/打开文件

  • mode:打开文件的模式常用的模式包括:

    • "r":只读模式。打开文件用于读取。注意:文件不存在表示打开文件失败。
    • "w":写入模式。文件内容截断为零长度,即清空文件内容,再写入内容。(如果文件不存在,系统会新建对应的文件)
    • "a":追加模式。不会清空原有文件内容,而在文件内容末尾追加。(如果文件不存在,系统会新建对应的文件)
    • 还有很多模式请查看手册:man fopen
  • 返回值

    • 打开文件后,将返回一个指向 FILE 类型的文件指针,指向打开的文件流。

    • 如果打开失败,则返回 NULL

请添加图片描述

请添加图片描述

为什么不带路径默认会在当前进程所在路径下创建文件?为什么不在其他路径创建文件?

首先大家需要明白一个道理:文件一定是由进程通过调用函数(如fopen())打开的,因此,文件与进程有关。所以,默认情况下,如果你不带路径,新建文件所处的路径将取决于进程当前的工作目录。

当然了,如果你的程序在运行时改变了当前工作目录,那么新建文件所处的路径也会相应地改变。

请添加图片描述

请添加图片描述

1.2 关闭文件 — fclose

C语言中,当你完成了对文件的读写操作后,最后应该使用fclose函数关闭文件。这是一个很重要的步骤,因为它确保了操作系统释放文件资源,并且在某些情况下,确保了写入的数据被刷新到磁盘上。不关闭文件可能会导致资源泄露或数据丢失

int fclose(FILE* stream);
  • 如果成功关闭文件,fclose函数返回0

  • 如果关闭失败,返回EOF-1),表示出现了错误。

1.3 文件写入 — fwrite

C语言对于文件写入有这几种方式:fputcfputsfwritefprintfsnprintf(大家可以去man手册查看用法)

int fputc (int character, FILE* stream);
int fputs (const char* str, FILE* stream);
int snprintf ( char* s, size_t n, const char* format, ...);

这里以fwrite函数为例

size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

这个函数将 nmemb 个元素,每个元素大小为 size 字节,从指针 ptr 所指向的内存块写入到给定的文件流 stream 中。

  • ptr:指向待写入数据的指针。

  • size:数据写入的字节数。

  • nmemb:以size为单位,待写入数据的数量。

  • stream:指向FILE对象的指针。

  • 返回值:如果返回值与 nmemb 不相等,说明写入时发生错误。

请添加图片描述

这里有一个细节问题:strlen需要+ 1’\0‘写入到log.txt文件中吗?

我们执行以上代码看看log.txt里的内容(不+ 1

请添加图片描述

然后我们再+ 1看看效果

请添加图片描述

后面多了一个类似于乱码的字符^@,这其实就是'\0'的二进制。因为vim是一个文本编辑器,对于二进制来说当然显示的是乱码啦。 这也说明了我们在向文件写入字符串的时候不需要处理'\0'(只需将字符串的内容写入即可),因为字符串以'\0'结尾是编程语言的规定,和文件没半毛钱关系 ~

接下来我们再连续执行程序,再看看log.txt的内容

请添加图片描述

我们不是向文件中写入了多个字符串hello file吗?为什么只写入了一个?

这是因为我们设置打开文件的模式是w,即在向文件写入之前会先对文件进行清空处理,然后再写入。

这不由得让我们联想到输出重定向>

请添加图片描述

因此输出重定向>的底层一定要以w的方式打开一个文件,即fopen("文件/文件路径", "w"),即将文件内容清空,最后再用写入函数进行向文件写入


接下来我们再以a的方式打开一个文件并写入

请添加图片描述

请添加图片描述

我们发现:a的方式打开一个文件并写入,不会清空文件,而是在文件内容末尾追加。

这不由得让我们联想到追加重定向>>

请添加图片描述

因此追加重定向>>的底层一定要以a的方式打开一个文件,即fopen("文件/文件路径", "a"),即不将文件内容清空,最后再用任意一个写入函数向文件内容进行追加

综上所述,输出重定向>和追加重定向>>主要区别就是在于打开文件的方式不同,一个以w,一个以a

请添加图片描述

二、文件系统调用

2.1 文件系统调用和函数的关系

我们知道,文件是在磁盘上存储的,磁盘作为外设(外部设备),所以访问文件其实也是在访问磁盘(硬件)!而我们计算机体系结构是分层的,如下所示:

在这里插入图片描述

而我们知道底层硬件一定要被操作系统管理的,作为普通用户无法直接访问底层硬件的相关信息,那么就只能通过管理者,也就是操作系统来获取,而操作系统根本不相信用户,因此提供了系统调用接口来访问,以此来保护操作系统。因此,几乎所有的库函数只要访问硬件设备,必定要封装系统调用接口。因此,以上文件操作的函数底层必定封装了文件系统调用接口!

2.2 打开文件 — open

Linux中,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:要打开或创建的文件的路径。不指定路径默认是当前进程的工作路径。

  • flags:控制文件打开方式。以下是常见的打开方式。(还有很多打开方式,可以通过man 2 open来查看)

    • O_WRONLY:以只写模式打开文件。
    • O_CREAT:如果文件不存在,则创建文件。
    • O_APPEND:在写入时将数据追加到文件的末尾。
    • O_TRUNC:清空文件内容。

    实际上,以上的选项是在<fcntl.h>头文件中定义的。我们可以在路径/usr/include/bits查看,此路径通常包含了与体系结构相关的宏定义和声明。

在这里插入图片描述

  • mode:用于指定新创建文件的权限,只有在 O_CREAT 标志位被设置时才有效。一般采用八进制表示的文件权限值,例如 0666

  • 返回值:

    • 如果成功,返回一个文件描述符(非负整数),该描述符可以在后续的读写操作中传参。

    • 如果失败,返回-1

请添加图片描述

请添加图片描述

当我们将程序运行起来,发现open打开文件失败,原因是:O_WRONLY只会读文件,而不会新建文件。那就非常简单了,再加一个宏O_CREAT,让文件不存在时自动创建即可。可是,open的第二个参数flag是整型,如何传两个参数?

这就要涉及到比特位方式的标志位传递方式

还记得诸如O_WRONLY这些宏吗?它们的共同特点:二进制序列中最多只有一个比特位上是1,并且1所处的比特位是不同的。而一个整型是4个字节,也就是32个比特位(标志位),那么我们可以使用按位或|运算符来组合多个标志位。比如0001 | 0010 = 0011。然后open函数内部实现可以使用按位与操作符&来检查是否设置了某个标志位,比如

请添加图片描述

请添加图片描述

因此,我们可以对一开始的代码进行修改,使用按位或|增加O_CREAT选项

请添加图片描述

请添加图片描述

如上,我们确实把一个不存在的文件log.txt创建好了,可是此文件的权限都是乱码,原因在于你新建一个文件,此文件要受Linux权限约束,你新建文件的权限必须得告诉操作系统。因此,只有在 O_CREAT 标志位被设置时,需要指定新文件的权限

在往期博客讲过,文件默认的权限是0666,即rw-rw-rw-

请添加图片描述

请添加图片描述

文件创建出来了,并且权限也不是乱码了,但是,权限并不是我们一开说的rw-rw-rw-。实际上创建出来的文件会收到文件默认权限掩码umask的影响实际创建出来文件的权限值为:mode & (~umask);而在Linux中,umask的默认权限值为0002,当我们设置mode值为0666时实际创建出来的文件权限为0666 & ~(0002) = 0664,即rw-rw-r--

若想创建出文件的权限值不受umask的影响,即如上想让权限为rw-rw-rw-,那么系统专门提供了 umask系统调用接口,可以让你修改默认的权限掩码。

即在创建文件前使用umask函数将文件掩码umask设置为0

请添加图片描述

请添加图片描述

2.3 文件关闭 — close

#include <unistd.h>
int close(int fd);
  • fd 是要关闭的文件描述符,即open函数的返回值。

  • 函数返回值为0表示成功关闭文件,返回-1表示出现错误

关闭文件描述符后,相关的系统资源将被释放,包括文件表项和文件描述符本身。这通常是在不再需要使用文件描述符时执行的操作,以释放系统资源并防止资源泄漏。

2.4 文件写入 — write

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
  • fd 是文件描述符(open函数返回值),表示要写入的文件。
  • buf 是一个指向要写入数据的缓冲区的指针。
  • count 是要写入的字节数。
  • 返回值是写入的字节数,如果出现错误,则返回-1

如果想要实现输出重定向的功能,不仅需要读O_WRONLY、创建文件O_CREAT,在写之前还需要将文件清空O_CREAT

请添加图片描述

请添加图片描述

如果想实现追加重定向也非常简单,由于清空和追加两个是冲突的,因此你只需要将清空O_CREAT替换成追加O_APPEND

请添加图片描述

请添加图片描述

【总结】

请添加图片描述

因此,只要是在 Linux 平台中编写的程序,无论是什么编程语言,在进行文件相关操作时,其文件操作函数都有封装系统调用接口。也就是说,要想与硬件(磁盘)打交道,必须经过 系统调用 -> 操作系统 -> 硬件 这条路线,否则无法直接与硬件进行交互。

三、文件描述符

3.1 什么是文件描述符

到目前为止,我们所知道的文件描述符就是open函数的返回值(非负整数),这个描述符可以在后续的读写操作中用作文件标识符,即找到对应的文件。

那不知道大家有没有思考过一个问题:为什么操作系统可以单单通过一个整数(文件描述符)就能找到对应的文件?

文件分为打开的文件没打开的文件(这里先讨论打开的文件,等到文件系统再谈没打开的文件)。打开的文件是由进程使用函数(如fopen())打开的。根据冯诺依曼体系结构,被打开的文件一定会被加载到内存。而一个进程可以打开多个文件(1:n),那么被打开的文件也一定要受操作系统的管理,那就要请出管理的六字真言:先描述,后组织。

  • 描述:一个被打开的文件一定要有自己的文件结构体对象file,包含文件的信息,即struct file { 文件信息; struct file* next; struct file* prev;};

  • 组织:通过双链表方式。

  • 往后对被打开文件的管理,就转换成为了对链表的增删改查!

所以,当进程打开文件时,操作系统会在内核中创建数据结构来描述这个已打开的文件对象(和PCB类似)。这个数据结构通常被称为 file 或其他类似的名字,它包含了文件的各种信息,如文件位置、权限、状态等。

而进程可以打开多个文件,那进程PCB结构体对象就要存储哪些文件是由哪一个进程打开的。因此,每个进程PCB对象都要和打开的文件建立关系!所以进程PCB对象其实有一个指针struct files_struct* files,这个指针指向结构体files_struct,而这个结构体包含一个指针数组struct file* fd_array[],这个数组我们可以称之为文件描述符表。数组中的每个元素都是指向当前进程所打开文件的指针(地址)!

所以,本质上文件描述符就是指针数组的下标(索引)。所以,只要拿着文件描述符,就可以找到对应的文件。

请添加图片描述

【源码】

请添加图片描述

我们可以打印出文件描述符来看看

请添加图片描述

请添加图片描述

3.2 三个标准流

如上,文件描述符即指针数组的下标是从3开始连续递增的。那这里我有一个问题:为何不从0开始递增呢?难道下标01、``2存储了其他被打开文件的地址吗?你的猜测是正确的!

在C语言中,默认情况下,当一个进程启动时,操作系统会打开三个标准流(本质上就是文件,不同的编程语言都会打开它的三个标准流)

  • stdin:标准输入,文件描述符 0。通常用来接收用户的输入,主要是键盘设备。

  • stdout:标准输出,文件描述符 1。通常用来输出程序的正常运行信息,主要是显示器设备。

  • stderr 标准错误,文件描述符 3。通常用来输出程序的错误信息,stderr 默认也将错误信息显示在终端上。主要也是显示器。

请添加图片描述

我们可以来验证:

请添加图片描述

FILEC语言封装文件操作的结构体,当你使用 C 标准库中的函数来操作文件时,这些库函数底层必定会封装系统调用接口,那么FILE结构体内部一定要有对应的文件描述符来与操作系统进行通信,即需要有文件描述符来调用底层封装的系统调用接口,因此FILE结构体里一定会包含文件描述符成员_fileno(其他编程语言的文件操作也是如此)

请添加图片描述

请添加图片描述

3.3 文件描述符的分配规则

如果是直接打开文件某个文件,由于系统默认会打开三个标准流,因此新分配的文件描述符从3开始依次递增。

请添加图片描述

请添加图片描述

如果我在打开log.txt文件之前,把标准输入stdin关闭,其文件描述符还会是3吗?

请添加图片描述

请添加图片描述

我们发现:新文件的文件描述符占据了标准输入stdin的文件描述符。因此,文件描述符的分配规则是:从头遍历文件描述符表fd_array[],找到一个最小的且没有被使用的下标,它的下标就是新文件的文件描述符(保证数组空间不会被浪费)。

3.4 引用计数

如果我这里故意将文件标识符1给关掉,即显示器文件被关闭,那么打印结果就无法显示到屏幕上

请添加图片描述

请添加图片描述

那如果我在文件标识符1关掉的基础上,再向stderr流写入,即向显示器写入,还能写入成功吗?有的人想,肯定不成功,文件标识符1关掉,等同将显示器文件关掉,而stderr也是将结果显示在显示器,肯定打印不出来

请添加图片描述

请添加图片描述

我们发现:结果可以打印出来。

其实每个文件对象都包含一个引用计数字段count,记录了有多少个文件描述符指向该文件对象。当一个进程打开一个文件时,内核会增加该文件对象的引用计数count++。当进程关闭文件描述符时,内核会减少相应文件对象的引用计数count--,并且将文件描述符表对应下标的位置置空NULL。只有当引用计数减少到零时,内核才会释放该文件对象及其相关资源。

在这里插入图片描述

四、再谈重定向

重定向实际上是改变了进程的文件描述符表中指针数组对应下标内容的指向。这样,进程在进行I/O操作时,就会按照新的文件描述符表中的指向来进行,从而实现了重定向的效果。

常见的重定向操作包括

  • 将一个文件描述符指向另一个文件描述符

  • 或者将一个文件描述符关闭。

4.1 输出重定向

输出重定向是指将程序本来输出到标准输出stdout设备的内容,重定向到其他文件

请添加图片描述

请添加图片描述

此时,我们发现,本来应该输出到显示器上的内容,输出到了文件log.txt当中,这不就是典型的输出重定向!

接下来我们来分析,为什么会出现这种情况

代码中,首先将stdout文件关闭,而后打开log.txt文件。根据文件描述符的分配规则是:从头遍历文件描述符表fd_array[],找到一个最小的且没有被使用的下标,它的下标就是新文件的文件描述符(保证数组空间不会被浪费)。那么log.txt的的文件描述符就是1,而后面写入文件是向文件描述符1中写入,即就是向log.txt文件写入。这就是重定向的原理。

请添加图片描述

4.2 追加重定向

追加重定向是指将程序本来输出到标准输出stdout设备的内容,追加到指定文件的末尾,而不是覆盖原文件内容。

那我们只需将以上代码的打开方式O_TRUNC修改成O_APPEND就有追加重定向的效果

请添加图片描述

请添加图片描述

4.3 输入重定向

输入重定向是指将程序从标准输入stdin中读取数据的方式改变为从其他文件读取数据

例如,让本应该从“键盘文件”读取数据,改从log.txt文件中读取数据,那么我们可以打开log.txt文件之前将stdin文件关闭,这样一来,根据文件描述符的分配规则,当我们后续打开log.txt文件时所分配到的文件描述符就是0

请添加图片描述

请添加图片描述

4.4 系统调用接口 — dup2

以上操作都需要先关闭再打开一个文件来实现重定向,操作过于繁琐,而且每次你写这样的代码都要向别人解释,因此操作系统提供了系统调用接口来实现重定向操作

#include <unistd.h>
int dup2(int oldfd, int newfd);        
  • oldfd :是要复制的文件描述符。

  • newfd :是要被覆盖的文件描述符。

  • 如果 newfd 已经打开,则操作系统首先会关闭 newfd,即释放文件对象。然后,dup2() 会使 newfd 指向 oldfd 所指向的文件对象。

下面是演示了如何使用 dup2() 函数将标准输出重定向到一个文件中:

请添加图片描述

请添加图片描述

下面是演示了如何使用 dup2() 函数将标准输出追加重定向到一个文件中:

请添加图片描述

请添加图片描述

下面是演示了如何使用 dup2() 函数将标准输入重定向到一个文件中:

请添加图片描述

请添加图片描述

为往期模拟实现bash添加重定向功能:点击跳转

五、补充知识

5.1 stdout和stderr的区别

我们至今所认知的标准输出流和标准错误流并没有区别,都是将数据向显示器打印。请添加图片描述

请添加图片描述

但若是将程序运行结果重定向输出到文件hello.txt当中,我们会发现hello.txt文件当中只将标准输出的打印语句重定向到hello.txt文件中,而向标准错误流输出的两行数据并没有重定向到文件当中,而是仍然输出到了显示器上。

请添加图片描述

实际上我们使用重定向时,默认重定向的是文件描述符是1的标准输出流,而并不会对文件描述符是2的标准错误流进行重定向。

此外,我们也可以将标准错误流进程重定向,通常使用 2>2>> 来重定向 stderr。详细命令如下:

./proc 1> hello.txt 2>stderr.txt 
//不要在重定向操作符和文件描述符之间添加空格。
// 1可以被省略,因为它是默认的输出流。

上述指令做了两次重定向,第一次把标准输出重定向到了文件描述符为1的显示器,第二次是把标准错误重定向到了文件描述符为2的显示器,,通过重定向可以分别将它们发送到不同的目的地,使得对程序输出和错误信息的处理更加灵活和有效。

前面已经提到,重定向只会默认把标准输出的进行处理,并不会重定向标准错误,如果我想让标准输出和标准错误一同混合起来到一个文件显示,如下命令:

./myfile 1>all.txt 2>&1

这个命令将 myfile的标准输出重定向到 all.txt 文件,2>&1 是将标准错误重定向到与标准输出相同的地方,也就是all.txt 文件中。

5.2 为什么Linux下一切皆文件(以源码的角度剖析)

一切皆文件其实是在Linux系统中一致访问资源的方式,即将所有资源抽象为文件。这包括了硬件设备等各种资源。(文件很容易理解,这里主要谈为什么硬件也要当做文件来看待)

当一个进程在Linux系统中运行时,操作系统会为每个文件创建一个文件对象struct file,这个文件对象跟踪了该文件的状态和相关信息。

因此,当一个进程打开硬件,那么操作系统就会管理这个硬件,即为该硬件创建文件对象struct file,而此文件对象其实提供了对设备的操作接口。

请添加图片描述

f_op指向对文件操作的各种函数指针。这些函数指针对应了文件的不同操作,例如读取、写入、定位等。

请添加图片描述

所以所谓的一切皆文件,就是相当于在文件这一层封装了一个文件对象,让文件对象中的函数指针结构体 file_operations 指向不同设备的操作方法。在这种情况下,不同类型的文件对象(比如普通文件、设备文件等)都具有相同的接口,即结构体file_operations。当应用程序调用文件操作函数时,操作系统根据文件对象的实际类型来调用相应的方法,这种行为类似于多态的表现,即同一个接口可以根据不同的对象类型展现出不同的行为。

因此,一切皆文件可以被视为在操作系统级别实现的一种多态思想,使得不同类型的资源都能够以统一的方式进行访问和管理。

六、相关代码

本篇博客相关代码:点击跳转

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

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

相关文章

OpenHarmony 实战开发——ArkUI容器类API介绍

容器类&#xff0c;顾名思义就是存储的类&#xff0c;用于存储各种数据类型的元素&#xff0c;并具备一系列处理数据元素的方法。在 ArkUI 开发框架中&#xff0c;容器类采用了类似静态的语言来实现&#xff0c;并通过 NAPI 框架对外提供。通过对存储位置以及属性的限制&#x…

如何找到MySQL中存储引擎所对应的表空间并且打开?

在上节课我们学习了数据库&#xff08;MySQL&#xff09;进阶&#xff1a;存储引擎&#xff0c;有不少同学产生疑惑&#xff0c;到底要怎么找到表空间并且打开啊&#xff1f;这节课我们就来探讨。 首先&#xff0c;根据这个路径&#xff1a;C:\ProgramData\MySQL\MySQL Server…

k8s的网路配置

目录 1、k8s相关网络类型 1.1 K8S中Pod网络通信 1.2 Overlay Network 1.3 VXLAN 1.3.1 vlan和vxlan的区别 2、Flannel 2.1 简介 2.2 Flannel工作原理 2.3 ETCD之Flannel提供说明 2.4 Flannel部署 2.4.1 在node节点上操作 2.4.2 在master01节点上操作 2.4.2.1 安装f…

SVDD(Singing Voice Deepfake Detection,歌声深度伪造检测)挑战2024

随着AI生成的歌声快速进步&#xff0c;现在能够逼真地模仿自然人类的歌声并与乐谱无缝对接&#xff0c;这引起了艺术家和音乐产业的高度关注。歌声与说话声不同&#xff0c;由于其音乐性质和强烈的背景音乐存在&#xff0c;检测伪造的歌声成为了一个特殊的领域。 SVDD挑战是首个…

Android实践:查看Activity信息

问题&#xff1a;本地Android SDK的monitor无法正常运行&#xff0c;看不了进程相关信息&#xff0c;确认当前显示Activity十分不便 解决办法&#xff1a;使用adb shell指令可以快速查看 命令&#xff1a; adb shell dumpsys activity activities 这个命令用于获取Android设…

精酿啤酒:品质的保障与消费者的信赖

在啤酒市场中&#xff0c;Fendi club啤酒以其卓着的品质和消费者的信赖赢得了广泛的认可。作为精酿啤酒的品牌&#xff0c;Fendi club啤酒始终坚持对品质的严格把控&#xff0c;为消费者带来放心的口感体验。 品质的保障源于Fendi club啤酒对原料的严谨挑选和加工。他们深知&a…

LeetCode刷题笔记第1800题:最大升序子数组和

LeetCode刷题笔记第1800题&#xff1a;最大升序子数组和 题目&#xff1a; 想法&#xff1a; 遍历数组的同时记录当前最大升序子数组和&#xff0c;最终返回最大升序子数组和 class Solution:def maxAscendingSum(self, nums: List[int]) -> int:result 0i 0n len(num…

前端之电力系统SVG图低代码

其实所有的图形都是由点&#xff0c;线&#xff0c;面组成的。点线面可以组成一个设备。下面就简单讲讲点线面是怎么画的吧 对于线&#xff0c;可以用path <g><path:d"M ${beginX},${beginY} L ${endX},${endY}":stroke-width"lineWidth":strok…

Java面试题:Spring框架除了IOC和AOP,还有哪些好玩的设计模式?

Spring是一个基于Java的企业级应用程序开发框架&#xff0c;它使用了多种设计模式来实现其各种特性和功能。本文将介绍一些在Spring中使用的常见设计模式以及相应的代码示例和说明。 单例模式 单例模式是Spring中最常用的设计模式之一。在ApplicationContext中&#xff0c;Bean…

DiskANN数据布局

_mem.index.data&#xff1a;和sift_base.fbin一模一样。0-3字节是总向量数&#xff0c;4-7是每个向量的特征数。后面就是依次放置的每个向量。 _disk.index&#xff1a;是存储的图&#xff0c;但是不光包含图也包含原始向量。前4KB不知道存的是啥。从第0x1000开始存放的是原始…

浙大博士毕业2年后发表观点:读博一定要慎重!

已经博士毕业两年了&#xff0c;非常高兴和大家分享自己研究生的学习和生活经历&#xff0c;给准备读研的同学和考研的同学一点点小帮助。 首先我简单介绍下自己&#xff0c;本科非 985&#xff0c;非 211&#xff0c;研究生非 985&#xff0c;非 211&#xff0c;就不说学校的名…

【FPGA、maltab】基于FPGA的SOQPSK调制解调技术的设计与实现

基于FPGA的SOQPSK调制解调技术的设计与实现 SOQPSK一、QPSK、OQPSK、SOQPSK之间的关系二、SOQPSK调制原理 matlab 仿真FPGA 实现顶层设计发射模块接收模块顶层调制解调FPGA代码 SOQPSK 一、QPSK、OQPSK、SOQPSK之间的关系 SOQPSK&#xff08;Shaped Offset Quadrature Phase …

fl studio试用版文件保存无法打开??一个方法教你免费打开!

前言 当下&#xff0c;各款编曲软件五花八门&#xff0c;而这其中最有声誉的必为FL Studio莫属 这个软件呢国人习惯叫他水果&#xff0c;拥有强大的录音、编曲、混音等功能&#xff0c;所以广受音乐圈欢迎。如今&#xff0c;大部分水果一旦有编曲所需&#xff0c;一般都要使用…

一文搞懂CPU是如何进行计算的?

你好&#xff0c;我是 shengjk1&#xff0c;多年大厂经验&#xff0c;努力构建 通俗易懂的、好玩的编程语言教程。 欢迎关注&#xff01;你会有如下收益&#xff1a; 了解大厂经验拥有和大厂相匹配的技术等 希望看什么&#xff0c;评论或者私信告诉我&#xff01; 文章目录 一…

优秀测试的核心能力!2招高效定位分析BUG!

之所以写这一篇文章&#xff0c;是突然想起来曾经在测试过程中被开发嘲讽过&#xff0c;事情是这样的&#xff0c;当时发现了一个疑似前端的Bug就草草提交到了禅道&#xff0c;结果刚来的女前端看到了就有点生气地问我为啥不查清到底是前后端问题就直接派给她前端了&#xff0c…

React框架-Next 学习-1

创建一个 Next.js 应用,node版本要高&#xff0c;16.5以上 npm淘宝镜像切为https://registry.npmmirror.com npm config set registry https://registry.npmmirror.com npx create-next-applatest//安装后 使用npm run dev 启动 Next.js 是围绕着 页面&#xff08;pages&am…

uac驱动之const修饰的变量和const修饰的指针

const int*p // p所指向的空间是常量 不可修改 ,但p可以修改 int*const p // p所指向的空间是可以修改 ,p不可以修改 #include <stdio.h> #include <string.h>struct usb_string {char id;const char *s; };enum {STR_ASSOC,STR_AC_IF,STR_USB_OUT_IT,STR_USB_O…

单位个人怎样向报社的报纸投稿?

作为一名单位的信息宣传员,我肩负着每月定期在媒体上投稿发表文章的重任。然而,在投稿的道路上,我经历了不少波折和挫折。 一开始,我天真地以为只要将稿件发送到报社的投稿邮箱,就能轻松完成任务。然而,现实却远比我想象的复杂。邮箱投稿的竞争异常激烈,编辑们会在众多稿件中挑…

ROS2系统与px4通信测试

参考文章&#xff1a; No communication with ROS2 using MicroXRCEAgent with px4 board ROS2官方安装及测试程序 概要 新安装的ROS2与PixHawk开发板进行通信。 操作步骤 启动示例程序&#xff0c;在&#xff5e;/ws_sensor_combined/src路径下执行&#xff1a; ros2 l…

MYSQL-9.问题排查

问题排查的思路与方向 问题排查思路 分析问题&#xff1a;根据理论知识经验分析问题&#xff0c;判断问题可能出现的位置或可能引起问题的原因&#xff0c;将目标缩小到一定范围&#xff1b;排查问题&#xff1a;基于上一步的结果&#xff0c;从引发问题的“可疑性”角度出发…