【Linux】基础IO--文件基础知识/文件操作/文件描述符

文章目录

  • 一、文件相关基础知识
  • 二、文件操作
    • 1.C语言文件操作
    • 2.操作系统文件操作
      • 2.1 比特位传递选项
      • 2.2 文件相关系统调用
      • 2.3 文件操作接口的使用
  • 三、文件描述符fd
    • 1.什么是文件描述符
    • 2.文件描述符的分配规则

一、文件相关基础知识

我们对文件有如下的认识:

1.文件 = 文件内容 + 文件属性,即文件包括文件的内容和属性两个部分

2.空文件只是文件的内容为空,但是文件的属性不为空,所以空文件也需要占据磁盘的空间

3.由于文件包括内容和属性,那么我们对文件的操作可以分为对文件内容非操作,对文件的属性进行操作以及对文件的内容和属性进行操作

3.Linux/windows中目录都采用多叉树的形式进行表示,即树的中间节点表示目录,树的叶子节点表示文件,所以我们可以使用文件路径+文件名来唯一的标识一个文件

4.在对文件进行访问的时候,如果没有指定文件的路径,那么默认在当前路径下对文件进行访问,当前路径指的是当前进程所在的工作路径

5.在C语言中,当我们把fopen,fclose,fwrite,fread等函数接口的程序编译链接形成可执行程序,如果我们运行该可执行程序,那么对应的函数就不会被调用,则对应的文件操作也就不会被执行,因为函数在运行时才会建立栈帧,所以对文件的操作本质上是进程对文件的操作

6.我们知道,我们要访问一个文件,那么就必须先打开这个文件,而文件存储在磁盘上,由于计算机结构的原因,磁盘上的文件必须通过OS才能和进程进行交互,所以文件打开是由用户进程和OS配合来完成的—用户进程调用文件的接口,OS系统实现这些系统调用接口

7.磁盘上存在许许多多的文件,并不是所有的这些文件都被打开了,所以文件分为被打开的文件和没有被打开的文件,那些没有被打开的文件被成为文件系统

所以,文件操作的本质是进程与被打开文件的关系

二、文件操作

在谈论文件操作之前,我们需要了解 语言层面上的文件操作与操作系统层面上的文件操作的关系

我们知道,每一种语言都有其对应的文件操作,包括面向过程语言C,面向对象语言C++/java,静态编译语言go,解释型语言python,甚至包括脚本语言shell等等,但是每一种语言对文件操作提供的接口都不相同,这样就会导致我们的学习成本变得很高。

站在语言的角度我们觉得是这样的,但是站在操作系统的角度就不是这样了 ,我们知道计算机的软硬件体系结构之后,就会知道操作系统为了同时满足 保护自身安全 与 为上层用户提供良好的(稳定的,安全的,高效的)服务,会给用户提供访问软硬件的系统调用接口,同时,为了降低用户使用成本,人们又在系统调用接口的基础上开发了用户操作接口,其中包括shell外壳与各种语言的函数库,而用户就是通过调用用户操作接口类完成指令,开发与管理等操作

在这里插入图片描述

也就是说,站在操作系统的角度,虽然每一种语言的文件操作接口都不一样,但是这些接口底层调用的一定是同一种系统调用接口,因为操作系统是计算机管理软硬件资源的软件,进程想要访问文件只能通过调用操作系统提供的系统调用接口,我们使用的fopen,fwrite,fclose等等接口底层也是调用系统调用接口

而系统调用接口只有一套,语言有无数种,每一种又不一样,那么我们学习文件操作只需要学习操作系统提供的系统调用中有关文件操作的接口即可,学习了系统调用就相当于学习了底层,以后我们再学习语言的文件操作时只需要学习一些新的方式即可,但是底层是不变的,这样就会大大降低学习的成本

1.C语言文件操作

在学习C语言的文件操作之前,我们先回顾一下C语言的操作函数

C语言文件操作接口

函数名函数功能
fopen打开指定文件
fclose关闭指定文件
fwrite以二进制的形式向文件中写入数据
fread以二进制的形式从文件中读取数据
fscanf把文件中的数据格式化的读取到内存中
fprintf把内存中的数据格式化的写入到文件中

总结:

  1. fopen:用于打开文件,并返回一个文件指针,可以指定不同的打开模式(如只读、只写、追加等)和文件类型(文本或二进制)。配合其他文件操作函数使用,如fprintffscanf等。

  2. fclose:用于关闭文件,关闭后文件指针指向的文件将不可访问。

  3. fread:从文件中读取数据到指定的内存缓冲区中,可以指定要读取的数据块的大小和数量。

  4. fwrite:将指定大小的数据块从内存缓冲区写入文件中,可以指定要写入的数据块的大小和数量。

  5. fseek:设置文件指针的位置,用于定位读写位置。可以通过设置相对于文件开头、文件末尾或当前位置的偏移量来移动文件指针。

  6. ftell:获取文件指针的当前位置,返回当前位置的偏移量。

  7. rewind:将文件指针重置到文件的起始位置,相当于调用fseek(file, 0, SEEK_SET)

  8. feof:检查文件指针是否已到达文件末尾,返回非零值表示到达文件末尾。

  9. fgets:从文件中读取一行数据到指定的字符数组中。

  10. fprintf:将格式化的数据写入文件中。

需要注意的是,在使用文件操作接口时,应当检查返回值以确保操作是否成功,并且在不需要使用文件时要及时关闭文件以释放资源。

C语言文件打开的几种方式

文件打开方式含义如果指定文件不存在
“r”(只读)为了输入数据,打开一个已经存在的文本文件出错
“w”(只写)为了输出数据,打开一个文本文件建立一个新的文件
“a”(追加)向文本文件尾部添加数据建立一个新的文件
“rb”(只读)为了输入数据,打开一个二进制文件出错
“wb”(只写)为了输出数据,打开一个二进制文件建立一个新的文件
“ab”(追加)向一个二进制文件尾部添加数据出错
“r+”(读写)为了读和写,打开一个文件文件出错
“w+”(读写)为了读和写,建立一个新的文件建立一个新的文件
“a+”(读写)打开一个文件,在文件的尾部进行读写建立一个新的文件

总结:

  1. r(只读):以只读方式打开文件。文件必须存在,否则打开失败。
  2. w(只写):以只写方式打开文件。如果文件存在,则文件内容会被截断为空;如果文件不存在,则会创建新文件。
  3. a(追加写):以追加写入方式打开文件。如果文件存在,则新的数据会追加到文件末尾;如果文件不存在,则会创建新文件。
  4. r+(读写):以读写方式打开文件。文件必须存在,允许读取和写入文件内容。
  5. w+(读写,创建新文件):以读写方式打开文件。如果文件存在,则文件内容会被截断为空;如果文件不存在,则会创建新文件。
  6. a+(追加读写):以追加读写方式打开文件。如果文件存在,则新的数据会追加到文件末尾;如果文件不存在,则会创建新文件。允许读取和写入文件内容。

需要注意的是,以上文件打开模式只适用于文本文件。对于二进制文件,可以在以上模式后添加b来表示二进制模式(例如rbwb等)。

此外,还有一些其他的文件打开模式,如rb+wb+等。这些模式在于读取和写入的组合方式,具体的操作方式和特性与上述模式类似,但会有一些细微的区别。

在选择文件打开模式时,需要根据具体的需求和操作来确定使用哪种模式。例如,如果需要只读取文件内容,则使用r模式;如果需要追加写入内容,则使用aa+模式;如果需要同时读取和写入文件内容,则使用r+w+a+模式,具体选择根据是否需要创建新文件或截断文件内容来决定。

C语言文件操作的例子

1.向文件中写入数据

#include <stdio.h>

#define FILE_NAME "log.txt"

int main()
{
    FILE *fp = fopen(FILE_NAME, "w");
    if (NULL == fp)
    {
        perror("fopen failed");
        return 1;
    }

    int cnt = 5;
    while (cnt)
    {
        fprintf(fp,"%s:%d\n","hello world",cnt--);
    }
    fclose(fp);
    
    return 0;
}

在这里插入图片描述

2.从文件中读取数据

#include <stdio.h>
#include <string.h>
#define FILE_NAME "log.txt"

int main()
{
    FILE *fp = fopen(FILE_NAME, "r");
    if (NULL == fp)
    {
        perror("fopen failed");
        return 1;
    }

    char buffer[64];
    while(fgets(buffer,sizeof (buffer)-1,fp)!=NULL)
    {
        buffer[strlen(buffer) - 1] = 0;
        puts(buffer);
    }
    
    fclose(fp);
    
    return 0;
}

在这里插入图片描述

注意:

1.r(只读),w(只写), r+(读写,不存在出错),w+(读写, 不存在创建), a(append, 追加), a+()

2.以w方式单纯的打开文件,无论是否写入数据,c语言会自动清空内部的数据

3.通过fwrite创建出来的文件log.txt,其权限是664,这是由于普通文件的默认权限为0666,linux默认的umask为0002,而文件的最终权限等于默认权限 & ~umask,所以log.txt的权限为0664

在这里插入图片描述

2.操作系统文件操作

2.1 比特位传递选项

C语言常通过一个整形来传递选项,但是当选项较多时,每一个选项都用一个整形来进行传递,那么函数的参数就会很多,这时就提出了使用一个比特位来传递一个选项,这样一个整形有32个比特位,就可以传递32种选项,多个传递时使用 | 运算即可,具体案例如下:

#include <stdio.h>

// 每一个宏只占用一个比特位,该比特位为1说明该选项成立,且各个宏的位置不重叠
#define OPTION_ONE (1 << 0)
#define OPTION_TWO (1 << 1)
#define OPTION_TREE (1 << 2)
#define OPTION_FOUR (1 << 3)

void show(int flags)
{
    // flags与上面哪个选项匹配,就执行对应的操作
    // 按位与的结果为1,说明flags对应的比特位为1
    if (flags & OPTION_ONE)
        printf("OPTION_ONE\n");
    if (flags & OPTION_TWO)
        printf("OPTION_TWO\n");
    if (flags & OPTION_TREE)
        printf("OPTION_TREE\n");
    if (flags & OPTION_FOUR)
        printf("OPTION_FOUR\n");
}

int main()
{
    // 主函数中通过传递不同的选项来达到不同的效果
    show(OPTION_ONE);
    printf("-----------------------\n");
    show(OPTION_TWO);
    printf("-----------------------\n");
    show(OPTION_ONE | OPTION_TWO);
    printf("-----------------------\n");
    show(OPTION_ONE | OPTION_TWO | OPTION_TREE);
    printf("-----------------------\n");
    show(OPTION_ONE | OPTION_TWO | OPTION_TREE | OPTION_FOUR);
    printf("-----------------------\n");

    return 0;
}

在这里插入图片描述

如上,我们将宏与比特位对应,然后在show函数中编写每一个宏对应的功能,之后我们就可以在其他函数中通过调用show函数并传递对应的选项来达到我们想要的结果,并且我们可以通过按位或来实现同时传递几个选项

2.2 文件相关系统调用

open & close

函数功能

open:打开或创建一个文件,

close:关闭一个文件

在这里插入图片描述

在这里插入图片描述

函数参数

int open(const char* pathname, int flags);
int open(const cahr *pathname, int flags, mode_t mode);
# 头文件:<sys/types.h>	 <sys/stat.h>  <fcntl.h> 
# pathname: 文件路径/文件名
# flags: 打开文件时,可以传入多个参数选项,用一个或者多个宏常量进行“或”运算,构成flags(比特位传递选项)
参数:
 O_RDONLY: 只读打开
 O_WRONLY: 只写打开
 O_RDWR : 读,写打开
 这三个常量,必须指定一个且只能指定一个
 O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限
 O_APPEND: 追加写
# mode: 指定创建新文件时文件的默认权限(文件最终权限还要受umask的影响)
# 函数返回值:int:文件打开或创建成功返回文件对应的文件描述符(整形),失败返回-1int close(int fd);
# 头文件:<unistd.h>
# fd:目标文件对应的文件描述符
# int:函数返回值,关闭成功返回0,关闭失败返回-1
文件打开方式-含义如果指定文件不存在
O_RDONLY以只读形式打开出错
O_WRONLY以只写形式打开出错
O_RDWR以读写形式打开出错
O_APPEND向文本文件尾添加数据出错
O_CREAT如果文件不存在,创建新文件建立一个新的文件
O_TRUNC打开文件时清空文件中之前的数据出错

上述这些宏表示不同的文件打开方式,其底层原理和我们上面讲的 通过比特位传递选项 是一样的,我们可以在调用 open 函数时传递一个或多个宏,来实现不同的文件打开方式。

同时,我们可以通过文件操作的系统调用接口和封装后的C语言文件操作接口还是存在很多细节上的不同的,如下:

C语言以 “w” 的方式打开文件,若文件不存在会自动创建一个新文件,而系统调用目标文件不存在直接报错,除非指定了 O_CREAT 选项;
C语言以 “w” 方式打开文件时会自动清空之前文件中的数据,而系统调用则是逐个字符进行覆盖,并不会提前清空文件中的数据,如果要清空必须指定 O_TRUNC 选项;
需要注意的是,O_CREAT 是一个建议性选项,即当文件存在时我们传递此选项也不会报错;同时,文件不存在创建文件时需要传递 mode 选项来指定新文件的访问权限;

上面这些细节的不同也从侧面印证了C语言文件操作接口是对系统调用接口的封装 – “w” 选项会自动清空旧数据、创建新文件,又比如创建新文件时C语言不用手动传递 mode 选项指定权限等等,这些细节都隐藏在了函数的具体实现中。

write 与 read

函数功能

write:向文件中写数据; read:从文件中读数据;

在这里插入图片描述

在这里插入图片描述

函数参数

ssize_t write(int fd, const void* buf, size_t count);
# 头文件:<unistd.h>
# fd:目标文件的文件描述符
# buf:要写入数据的来源
# count:要写入数据的字节数
# ssize_t:函数返回值,写入成功返回成功写入的字节数,写入失败返回-1

ssize_t read(int fd, void* buf, size_t count);
# 头文件:<unistd.h>
# fd:目标文件的文件描述符
# buf:读取数据存放的位置
# count:要读取数据的字节数
# ssize_t:函数返回值,读取成功返回读取写入的字节数,读到文件末尾返回0,读取失败返回-1

2.3 文件操作接口的使用

操作系统系统调用文件相关接口的使用和C语言文件操作接口的使用总体上是差不多的,只是一些细节上有所不同。

1.向文件中写数据

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>

#define FILE_NAME "log.txt"

int main()
{
    // 创建文件并以只写形式打开,并指定文件的默认权限为0666(还受umask的影响)
    // 同时,我们可以通过umask接口手动设置当前进程的文件掩码,而不使用从父进程继承过来的umask
    umask(0000);
    int fd = open(FILE_NAME, O_WRONLY | O_CREAT, 0666);
    if (fd < 0)
    {
        perror("open failed");
        return 1;
    }

    int cnt = 5;
    char buffer[64];

    while (cnt)
    {
        sprintf(buffer, "%s:%d\n", "hello world", cnt--);
        // 注意:这里strlen求得的长度不用加1,因为字符串以'\0'结尾只是C语言的特性,而文件中并不这样规定
        write(fd, buffer, strlen(buffer));
    }

    close(fd);
    return 0;
}

注意:

1.建 文件时我们通过 umask 系统调用将 umask 设置为了 0000(第一个0代表八进制),然后将 mode 设置为 0666,所以 文件的最终权限为 默认权限 & ~umask – 0666 & ~0000 = 0666;

在这里插入图片描述

在这里插入图片描述

2.向文件中写入数据时如果不指定 O_TRUNC 选项,新数据就会逐字节覆盖原数据,所以有时候就会出现下面只覆盖了一部分原数据的情况:

在这里插入图片描述

3.C语言中字符串以 ‘\0’ 结尾,但是文件中字符串并不以 ‘\0’ 结尾,所以我们向文件中写入字符串时,count 设置为 strlen(str) 就行,不用把最后面的 ‘\0’ 字符加上,如果加上了就会出现部分乱码:

在这里插入图片描述

2.从文件中读数据

#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>

#define FILE_NAME "log.txt"

int main()
{
    // 创建文件并以只写形式打开,并指定文件的默认权限为0666(还受umask的影响)
    // 同时,我们可以通过umask接口手动设置当前进程的文件掩码,而不使用从父进程继承过来的umask
    umask(0000);
    int fd = open(FILE_NAME, RD_ONLY, 0666);
    if (fd < 0)
    {
        perror("open failed");
        return 1;
    }

    char buffer[1024];
    ssize_t num = read(fd, buffer, sizeof(buffer) - 1);
    if (num > 0)
        buffer[num] = 0; // 0, '\0', NULL -> 0
    printf("%s", buffer);
    close(fd);
    return 0;
}

在这里插入图片描述

由于C语言字符串以 ‘\0’ 结尾,而文件中的字符串数据并不包含 ‘\0’,所以这里我们需要预留一个位置,便于在数据量大于等于1024字节这种极端情况下 buffer中仍有空间来放置 ‘\0’。

三、文件描述符fd

1.什么是文件描述符

我们知道,文件操作本质上是进程与被打开文件之间的关系,同时,一个进程可以打开多个文件,且操作系统同时运行着许多个进程;那么操作系统中就一定存在着大量被打开的文件,那这些被打开的文件要不要被操作系统管理起来呢?答案是肯定的。

如何管理呢? 答案是先描述,再组织,即将文件的所有属性都总结到一个结构体中,并为每一个文件都创建一个结构体对象,再用一种数据结构将这些结构体对象组织起来,这样对众多被打开文件的管理就变成了对某一种数据结构的增删查改;Linux 中用于管理文件的内核数据结构叫做 struct file {} 结构体,其中包含了文件的大部分属性。

进程如何知道哪些被打开文件属于它呢?如图:

在这里插入图片描述

进程的 task_struct 里面有一个 struct files_struct *files 指针变量,它指向一个属于该进程的数据结构对象 struct files_struct,该对象里面包含了一个指针数组 struct file* fd_array[],即进程的文件描述符表,数组里面的每个元素都是指针,指向一个 struct file 对象,而这个数组的下标就是我们用户得到的文件描述符 fd。

也就是说,进程可以通过进程控制块中的 files 变量找到 files_struct 结构体,再通过 files_struct 中的文件描述符表具体下标中保存的地址找到具体文件的内核数据结构 file,从而实现数据的读取与写入。

总结:现在知道,文件描述符就是从0开始的小整数,当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件,于是就有了 file 结构体,表示一个已经打开的文件对象。而进程执行 open 系统调用,所以必须让进程和文件关联起来,于是每个进程都有一个 *files 指针,指向一张表 files_struct,该表最重要的部分就是包含一个指针数组,数组中每个元素都是一个指向打开文件的指针。所以,本质上,文件描述符就是该数组的下标,因此,只要拿着文件描述符,就可以找到对应的文件。
所以,文件描述符是从0开始的小整数,其本质是文件描述符表中的数组下标。

2.文件描述符的分配规则

我们知道了文件描述符是什么,那么文件描述符是如何进行分配的呢?

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>

#define FILE_NAME(number) "log.txt"#number


int main()
{
    int fd1 = open(FILE_NAME(1),O_WRONLY | O_CREAT | O_TRUNC,0666);
    int fd2 = open(FILE_NAME(2),O_WRONLY | O_CREAT | O_TRUNC,0666);
    int fd3 = open(FILE_NAME(3),O_WRONLY | O_CREAT | O_TRUNC,0666);
    int fd4 = open(FILE_NAME(4),O_WRONLY | O_CREAT | O_TRUNC,0666);
    int fd5 = open(FILE_NAME(5),O_WRONLY | O_CREAT | O_TRUNC,0666);

    printf("fd1 : %d\n",fd1);
    printf("fd2 : %d\n",fd2);
    printf("fd3 : %d\n",fd3);
    printf("fd4 : %d\n",fd4);
    printf("fd5 : %d\n",fd5);

    close(fd1);
    close(fd2);
    close(fd3);
    close(fd4);
    close(fd5);

    return 0;
}

在这里插入图片描述

注:C语言 # 在宏当中的作用 – 将参数插入到字符串中。

从运行结果可以看到,文件描述符是连续分配且依次增大的,这也很合理,因为文件描述符本质上是数组下标,而连续增长正好是数组下标的特性;但是这里有一个很奇怪的地方 – 文件描述符是从3开始的,那么0、1、2号下标呢?这是由三个默认打开的标准流引起的。

标准输入、标准输出与标准错误流

我们在运行一个程序的时候,操作系统会自动为我们打开三个流 – 标准输入流 stdin、标准输出流 stdout、标准错误流 stderr,它们分别对应键盘文件、显示器文件与显示器文件,其文件描述符分别是 0号、1号和2号,所以我们打开其他文件时 fd 默认是从3号开始分配的。即Linux进程默认情况下会有3个缺省打开的文件描述符,分别是标准输入0, 标准输出1, 标准错误2.0,1,2对应的物理设备一般是:键盘,显示器,显示器

#include <stdio.h>

int main() {
    printf("stdin->fd:%d\n", stdin->_fileno);
    printf("stdout->fd:%d\n", stdout->_fileno);
    printf("stderr->fd:%d\n", stderr->_fileno);
    return 0;
}

在这里插入图片描述

注:Linux 系统调用 open 接口的返回值是文件描述符 fd,而C语言 fopen 接口的返回值是 FILE*,其中 FILE 是一个结构体类型;我们知道,fopen 底层调用的是 open 接口,而 fopen 又不使用 fd 作为函数返回值,那么 FILE 结构体里面就一定会封装一个变量来表示 fd;gcc 中这个变量是 _fileno;
既然系统默认打开三个文件,那么我们可不可以将其关闭呢?当然可以:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>

#define FILE_NAME(number) "log.txt"#number

int main()
{
    close(0);
    close(2);
    int fd1 = open(FILE_NAME(1),O_WRONLY | O_CREAT | O_TRUNC,0666);
    int fd2 = open(FILE_NAME(2),O_WRONLY | O_CREAT | O_TRUNC,0666);
    int fd3 = open(FILE_NAME(3),O_WRONLY | O_CREAT | O_TRUNC,0666);
    int fd4 = open(FILE_NAME(4),O_WRONLY | O_CREAT | O_TRUNC,0666);
    int fd5 = open(FILE_NAME(5),O_WRONLY | O_CREAT | O_TRUNC,0666);

    printf("fd1 : %d\n",fd1);
    printf("fd2 : %d\n",fd2);
    printf("fd3 : %d\n",fd3);
    printf("fd4 : %d\n",fd4);
    printf("fd5 : %d\n",fd5);

    close(fd1);
    close(fd2);
    close(fd3);
    close(fd4);
    close(fd5);

    return 0;
}

在这里插入图片描述

可以看到,当0号和2号文件描述符被关闭以后,系统将其分配给了新打开的文件 log.txt1 和 log.txt2。

注:close 关闭文件并不是将 fd 指向的 file 对象释放掉,而仅仅是让当前进程文件描述符表中的对应下标不再指向该 file 对象,因为同一个文件可能会被多个进程访问,特别是父子进程。

(其底层可以采用 f_count **引用计数 **的方式来实现,即当有指向该文件的进程关闭时文件计数减1,有指向该文件的进程打开时文件计数加1,当 f_count 为 0 时操作系统才释放该文件的内核数据结构,即真正意义上的关闭文件)

所以,文件描述符的分配规则是:从小到大依次搜寻,寻找未被使用的最小 fd 作为新打开文件的 fd,即在files_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

java设计模式学习之【建造者模式】

文章目录 引言建造者模式简介定义与用途实现方式&#xff1a; 使用场景优势与劣势建造者模式在spring中的应用CD&#xff08;光盘&#xff09;的模拟示例UML 订单系统的模拟示例UML 代码地址 引言 建造者模式在创建复杂对象时展现出其强大的能力&#xff0c;特别是当这些对象需…

带大家做一个,易上手的家常炒鸡蛋

想做这道菜 先准备五个鸡蛋 然后将鸡蛋打到碗里面 然后 加小半勺盐 这个看个人喜好 放多少都没问题 不要太咸就好 将鸡蛋搅拌均匀 起锅烧油 油温热了之后 放三个干辣椒进去炒 干辣椒烧黑后 捞出来 味道就留在油里了 然后 倒入鸡蛋液 翻炒 注意翻炒 不要粘锅底 或者 一面糊…

嵌入式Linux:ARM驱动+QT应用+OpenCV人脸识别项目实现

一、前言&#xff1a; 这个项目主要分为两部分&#xff0c;客户端&#xff08;ARM板端&#xff09;负责利用OpenCV采集人脸数据&#xff0c;利用TCP将人脸数据发送给服务器&#xff0c;然后服务器根据人脸数据进行人脸识别&#xff0c;将识别后的结果返还给客户端&#xff0c;客…

【Linux】环境变量

文章目录 1. 环境变量的概念2. 常见的环境变量3. 查看环境变量的方法4. 测试PATH5. 和环境变量有关的命令6. 环境变量的组织方式7. 通过代码获取环境变量7.1 通过命令行的第三个参数7.2 通过第三方变量environ 8. 通过系统调用获取9. 环境变量的全局属性 1. 环境变量的概念 **…

函数指针数组指针数组传参的本质字符指针(进阶篇)

&#x1f680; 作者&#xff1a;阿辉不一般 &#x1f680; 你说呢&#xff1a;不服输的你&#xff0c;他们拿什么赢 &#x1f680; 专栏&#xff1a;爱上C语言 &#x1f680;作图工具&#xff1a;draw.io(免费开源的作图网站) 如果觉得文章对你有帮助的话&#xff0c;还请点赞…

【带头学C++】----- 八、C++面向对象编程 ---- 8.5 struct结构体类型增强使用说明

目录 8.5 struct结构体类型增强使用说明 8.5.1 C结构体可以定义成员函数 8.5.2 c中定义结构体变量可以不加struct关键字 8.6 bool布尔类型关键字 8.5 struct结构体类型增强使用说明 第六章对结构体的使用、内存对齐以及数组、深拷贝和浅拷贝进行了一个详细的说明&#xff0c…

带键扫的LED专用驱动方案

一、基本概述 TM1650 是一种带键盘扫描接口的LED&#xff08;发光二极管显示器&#xff09;驱动控制专用电路。内部集成有MCU输入输出控制数字接口、数据锁存器、LED 驱动、键盘扫描、辉度调节等电路。TM1650 性能稳定、质量可靠、抗干扰能力强&#xff0c;可适用于24 小时长期…

对抗产品团队中的认知偏误:给产品经理的专家建议

今天的产品经理面临着独特的挑战。他们不仅需要设计和构建创新功能&#xff0c;还必须了解这些功能将如何为客户带来价值并推进关键业务目标。如果不加以控制&#xff0c;认知偏差可能会导致您构建的内容与客户想要的内容或业务需求之间不一致。本文将详细阐述产品经理可以避免…

【EI会议征稿】第四届应用数学、建模与智能计算国际学术会议(CAMMIC 2024)

第四届应用数学、建模与智能计算国际学术会议&#xff08;CAMMIC 2024&#xff09; 2024 4th International Conference on Applied Mathematics, Modelling and Intelligent Computing 第四届应用数学、建模与智能计算国际学术会议&#xff08;CAMMIC 2024&#xff09;将于…

超详细!Opencv人脸识别!附源码!

一、新建环境 注意&#xff01;&#xff01;确定后需要关闭项目&#xff0c;重新打开&#xff0c;终端的环境才会变化&#xff01;&#xff01; 二、下载安装包&#xff08;只需要3个即可&#xff09; 1. 下载dlib包 pip install dlib-19.19.0-cp38-cp38-win_amd64.whl.whl …

XTU OJ 1339 Interprime 学习笔记

链接 传送门 代码 #include<bits/stdc.h> using namespace std;const int N1e610; //78498 我计算了一下&#xff0c;6个0的范围内有这么多个素数&#xff0c;所以开这么大的数组存素数 //计算的代码是一个循环 int prime[80000]; int a[N],s[N];//s数组是前缀和数组b…

力扣 --- 删除有序数组中的重复项 II

题目描述&#xff1a; 给你一个有序数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使得出现次数超过两次的元素只出现两次 &#xff0c;返回删除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的…

时序预测 | Python实现GA-TCN-LSTM遗传算法-时间卷积神经网络-长短期记忆网络时间序列预测

时序预测 | Python实现GA-TCN-LSTM遗传算法-时间卷积神经网络-长短期记忆网络时间序列预测 目录 时序预测 | Python实现GA-TCN-LSTM遗传算法-时间卷积神经网络-长短期记忆网络时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 使用先进的机器学习技术和优化算法…

Ubuntu systemd-analyze命令(系统启动性能分析工具:分析系统启动时间,找出可能导致启动缓慢的原因)

文章目录 Ubuntu systemd-analyze命令剖析目录简介systemd与systemd-analyze工作原理 安装和使用命令参数详解用例与示例显示启动时间&#xff08;systemd-analyze time&#xff09;列出启动过程中各个服务的启动时间&#xff08;systemd-analyze blame&#xff09;显示系统启动…

没想法、没经验做不了BI?求你,别再自我pua了

“我没想法&#xff0c;没经验&#xff0c;做不了BI报表&#xff0c;是不是不适合现代职场啊&#xff01;”在网上看到不少姐妹有这种迷惘发言&#xff0c;真的就&#xff0c;恨铁不成钢。求你了&#xff0c;别再自我pua了。你没想法没经验多正常啊&#xff0c;因为你没用过BI报…

Django 模板引擎 (四)

一、Django模板引擎 一个强大的工具&#xff0c;用于在HTML页面中嵌入动态内容。它使用一种被称为Django模板语言&#xff08;Django Template Language&#xff09;的简单而强大的语法来处理模板。该模板语言使用”{% %}”进行标记&#xff0c;用于执行各种操作。 二、Django…

指针数组以及利用函数指针来实现简易计算器及typedef关键字(指针终篇)

文章目录 &#x1f680;前言&#x1f680;两段有趣的代码✈️typedef关键字 &#x1f680;指针数组&#x1f680;简易计算器的实现 &#x1f680;前言 基于阿辉前两篇博客指针的基础篇和进阶篇对于指针的了解&#xff0c;那么今天阿辉将为大家介绍C语言的指针剩下的部分&#…

计算机组成原理期中题库

计算机组成原理题目集 2.1 下面是关于计算机中存储器容量单位的叙述&#xff0c;其中错误的是 A. 最基本的计量单位是字节&#xff08;Byte&#xff09;&#xff0c;一个字节等于8bit B. 一台计算机的编址单位、指令字长和数据字长都一样&#xff0c;且是字节的整数倍 C. 最小…

零基础编程入门视频教程,零基础编程从哪学起,分享中文编程工具构件实例

零基础编程入门视频教程&#xff0c;零基础编程从哪学起&#xff0c;分享中文编程工具构件实例 1、零基础编程入门视频教程&#xff0c;系统化编程教程链接 https://jywxz.blog.csdn.net/article/details/134073098?spm1001.2014.3001.5502 2、零基础编程从哪学起 建议初学…

多多跨境跑出高质量发展“加速度”,解锁拼多多Q3财报背后的王牌

互联网红利渐趋消退&#xff0c;用户拉新难度加大&#xff0c;这些现象也在表明过去电子商务依靠资本、流量快速增长的发展模式已经成为过去式。由高速发展转为高质量发展&#xff0c;在今天每一个经济体与宏观经济发展态势一般&#xff0c;发展的“质量”价值正在被放大开来。…