深入C语言文件操作:从库函数到系统调用

引言

文件操作是编程中不可或缺的一部分,尤其在C语言中,文件操作不仅是处理数据的基本手段,也是连接程序与外部世界的重要桥梁。C语言提供了丰富的库函数来处理文件,如 fopenfclosefreadfwrite 等。然而,这些库函数实际上是基于操作系统提供的系统调用构建的。理解库函数和系统调用之间的关系,不仅有助于编写高效的代码,还能帮助我们更好地理解底层操作系统的机制。

本文将深入探讨C语言文件操作中的库函数和系统调用,解释它们的工作原理、区别和联系,并通过实际示例展示如何使用这些函数。
在这里插入图片描述

C标准库函数与系统调用概述

6.1 C标准库函数

C标准库函数是ANSI C标准中定义的一组函数,它们提供了一种跨平台的方式来处理文件操作。这些函数通常在 stdio.h 头文件中声明,并且在大多数操作系统中都有实现。常见的文件操作库函数包括:

  • fopen:打开文件。
  • fclose:关闭文件。
  • fread:从文件中读取数据。
  • fwrite:向文件中写入数据。
  • fgetc:从文件中读取一个字符。
  • fputc:向文件中写入一个字符。
  • fgets:从文件中读取一行。
  • fputs:向文件中写入一行。
  • fseek:移动文件指针。
  • ftell:获取文件指针的当前位置。
  • rewind:将文件指针重置到文件开头。

6.2 系统调用

系统调用是操作系统提供给用户程序的一组接口,用于请求操作系统执行特定的低级操作。系统调用通常在内核态执行,提供了对硬件设备的直接访问。常见的文件操作系统调用包括:

  • open:打开文件。
  • close:关闭文件。
  • read:从文件中读取数据。
  • write:向文件中写入数据。
  • lseek:移动文件指针。
  • ioctl:控制设备。

库函数与系统调用的区别

6.3 工作空间不同

  • 库函数:运行在用户态,通常包含在标准库中,如 glibc
  • 系统调用:运行在内核态,由操作系统内核提供。

6.4 缓冲机制不同

  • 库函数:通常使用缓冲机制来提高性能。例如,freadfwrite 会先将数据读取到内存缓冲区,然后再批量处理。
  • 系统调用:不使用缓冲机制,每次调用都会直接与文件系统交互。

6.5 可移植性不同

  • 库函数:具有良好的可移植性,可以在不同的操作系统上使用相同的接口。
  • 系统调用:依赖于特定的操作系统,不同操作系统的系统调用接口可能不同。

6.6 性能差异

  • 库函数:由于使用了缓冲机制,减少了用户态和内核态之间的切换次数,通常性能更高。
  • 系统调用:每次调用都会导致用户态和内核态之间的切换,性能较低。

库函数与系统调用的联系

尽管库函数和系统调用在许多方面有所不同,但它们之间存在着密切的联系。实际上,许多库函数最终会调用系统调用来完成实际的文件操作。

6.7 fopenopen

fopen 函数用于打开文件,并返回一个指向 FILE 结构的指针。fopen 实际上调用了 open 系统调用。

示例代码:

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>int main() {// 使用 fopen 打开文件FILE *file = fopen("example.txt", "r");if (file == NULL) {fprintf(stderr, "打开文件失败: %s\n", strerror(errno));return 1;}// 使用 open 系统调用打开文件int fd = open("example.txt", O_RDONLY);if (fd == -1) {perror("打开文件失败");return 1;}fclose(file);close(fd);return 0;
}

6.8 freadread

fread 函数用于从文件中读取数据,并返回实际读取的数据项数。fread 实际上调用了 read 系统调用。

示例代码:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>int main() {// 使用 fopen 打开文件FILE *file = fopen("example.txt", "r");if (file == NULL) {fprintf(stderr, "打开文件失败: %s\n", strerror(errno));return 1;}// 使用 open 系统调用打开文件int fd = open("example.txt", O_RDONLY);if (fd == -1) {perror("打开文件失败");return 1;}char buffer[100];size_t bytes_read;// 使用 fread 读取文件bytes_read = fread(buffer, 1, sizeof(buffer), file);if (ferror(file)) {fprintf(stderr, "读取文件失败: %s\n", strerror(errno));fclose(file);close(fd);return 1;}buffer[bytes_read] = '\0';printf("fread: %s\n", buffer);// 使用 read 系统调用读取文件bytes_read = read(fd, buffer, sizeof(buffer));if (bytes_read == -1) {perror("读取文件失败");close(fd);return 1;}buffer[bytes_read] = '\0';printf("read: %s\n", buffer);fclose(file);close(fd);return 0;
}

6.9 fwritewrite

fwrite 函数用于向文件中写入数据,并返回实际写入的数据项数。fwrite 实际上调用了 write 系统调用。

示例代码:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>int main() {// 使用 fopen 打开文件FILE *file = fopen("example.txt", "w");if (file == NULL) {fprintf(stderr, "打开文件失败: %s\n", strerror(errno));return 1;}// 使用 open 系统调用打开文件int fd = open("example.txt", O_WRONLY | O_CREAT, 0644);if (fd == -1) {perror("打开文件失败");return 1;}char *message = "Hello, World!\n";// 使用 fwrite 写入文件if (fwrite(message, 1, strlen(message), file) != strlen(message)) {if (ferror(file)) {fprintf(stderr, "写入文件失败: %s\n", strerror(errno));fclose(file);close(fd);return 1;}}// 使用 write 系统调用写入文件if (write(fd, message, strlen(message)) == -1) {perror("写入文件失败");close(fd);return 1;}fclose(file);close(fd);return 0;
}

6.10 fcloseclose

fclose 函数用于关闭文件,并刷新缓冲区。fclose 实际上调用了 close 系统调用。

示例代码:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>int main() {// 使用 fopen 打开文件FILE *file = fopen("example.txt", "w");if (file == NULL) {fprintf(stderr, "打开文件失败: %s\n", strerror(errno));return 1;}// 使用 open 系统调用打开文件int fd = open("example.txt", O_WRONLY | O_CREAT, 0644);if (fd == -1) {perror("打开文件失败");return 1;}char *message = "Hello, World!\n";// 使用 fwrite 写入文件if (fwrite(message, 1, strlen(message), file) != strlen(message)) {if (ferror(file)) {fprintf(stderr, "写入文件失败: %s\n", strerror(errno));fclose(file);close(fd);return 1;}}// 使用 write 系统调用写入文件if (write(fd, message, strlen(message)) == -1) {perror("写入文件失败");close(fd);return 1;}// 使用 fclose 关闭文件if (fclose(file) != 0) {fprintf(stderr, "关闭文件失败: %s\n", strerror(errno));close(fd);return 1;}// 使用 close 系统调用关闭文件if (close(fd) == -1) {perror("关闭文件失败");return 1;}return 0;
}

文件描述符与缓冲区

6.11 文件描述符

文件描述符(File Descriptor,FD)是一个用于引用打开文件和其他类型的I/O资源的整数。每个进程都有自己的文件描述符表,用于跟踪进程打开的所有文件和I/O资源。

  • 唯一标识:文件描述符为每个打开的文件或I/O资源提供了一个唯一的标识符,通常是一个非负整数。
  • 文件描述符表:每个进程都有自己的文件描述符表,这是一个内核数据结构,用于跟踪进程打开的所有文件和I/O资源。
  • 系统调用:文件描述符通常通过系统调用如 openreadwriteclose 等进行操作。open 调用返回一个新的文件描述符,readwrite 使用文件描述符来读取或写入数据,而 close 用于释放文件描述符。
  • 标准流:Linux为标准输入(stdin)、标准输出(stdout)和标准错误(stderr)分别分配了文件描述符0、1和2。
  • 缓冲机制:Linux内核可能会对通过文件描述符进行的I/O操作使用缓冲机制,以提高性能和减少实际的磁盘I/O操作。
  • 错误处理:当系统调用失败时,会返回-1,并且全局变量 errno 被设置为表示错误的特定值。
  • 多路复用:文件描述符可以用于I/O多路复用机制,如 selectpollepoll,允许进程同时监控多个文件描述符上的I/O状态。
  • 继承性:当创建新进程时,子进程会继承父进程的文件描述符表中的文件描述符,除非它们在子进程中被显式地关闭。
  • 重定向:文件描述符可以通过 dupdup2 等函数进行重定向,允许将一个文件描述符的引用复制到另一个文件描述符上。
  • 文件锁:文件描述符可以用于对文件加锁,以控制对文件的并发访问。

6.12 缓冲区机制

缓冲区机制是C标准库中用于提高I/O性能的一种技术。缓冲区可以减少用户态和内核态之间的切换次数,从而提高性能。

  • 全缓冲:对于文件,通常是全缓冲的。这意味着数据会先写入缓冲区,当缓冲区满或文件关闭时,数据才会被写入文件。
  • 行缓冲:对于终端输入输出,通常是行缓冲的。这意味着数据会在遇到换行符时被写入文件。
  • 无缓冲:对于标准错误输出,通常是无缓冲的。这意味着数据会立即被写入文件。

实际应用案例

6.13 文件拷贝示例

下面是一个使用库函数和系统调用实现文件拷贝的示例。该示例展示了如何结合使用库函数和系统调用来完成文件操作。

示例代码:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>int copy_file(const char *src_path, const char *dst_path) {// 使用 fopen 打开源文件FILE *src_file = fopen(src_path, "rb");if (src_file == NULL) {fprintf(stderr, "打开源文件失败: %s\n", strerror(errno));return 1;}// 使用 open 系统调用打开目标文件int dst_fd = open(dst_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);if (dst_fd == -1) {fprintf(stderr, "打开目标文件失败: %s\n", strerror(errno));fclose(src_file);return 1;}char buffer[1024];size_t bytes_read;while ((bytes_read = fread(buffer, 1, sizeof(buffer), src_file)) > 0) {if (ferror(src_file)) {fprintf(stderr, "读取源文件失败: %s\n", strerror(errno));fclose(src_file);close(dst_fd);return 1;}if (write(dst_fd, buffer, bytes_read) == -1) {perror("写入目标文件失败");fclose(src_file);close(dst_fd);return 1;}}if (ferror(src_file)) {fprintf(stderr, "读取源文件失败: %s\n", strerror(errno));fclose(src_file);close(dst_fd);return 1;}if (fclose(src_file) != 0) {fprintf(stderr, "关闭源文件失败: %s\n", strerror(errno));close(dst_fd);return 1;}if (close(dst_fd) == -1) {perror("关闭目标文件失败");return 1;}return 0;
}int main() {const char *src_path = "source.txt";const char *dst_path = "destination.txt";if (copy_file(src_path, dst_path) == 0) {printf("文件复制成功\n");} else {printf("文件复制失败\n");}return 0;
}

文件操作的底层原理

6.14 文件描述符与文件信息区

每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字、文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是由系统声明的,取名 FILE

每当打开一个文件的时候,系统会根据文件的情况自动创建一个 FILE 结构的变量,并填充其中的信息,使用者不必关心细节。一般都是通过一个 FILE 的指针来维护这个 FILE 结构的变量,这样使用起来更加方便。

6.15 文件指针的位置

文件指针可以指向文件的任意位置,通常用来记录下一次读取或写入的位置。可以通过一些函数来移动文件指针的位置,如 fseek 函数和 rewind 函数。可以通过一些函数来获取当前文件指针的位置,如 ftell 函数。

6.16 文件的打开和关闭

在使用文件之前应该打开文件,使用完之后应该关闭文件。ANSI C 规定使用 fopen 来打开文件,用 fclose 来关闭文件。

文件打开方式 mode 参数说明:

  • "r":只读模式,打开一个已经存在的文本文件,用于输入。
  • "w":只写模式,打开一个文本文件用于输出,如果文件已存在则清空原有内容,如果文件不存在则创建新文件。
  • "a":追加模式,打开一个文本文件用于在文件尾部追加数据,如果文件不存在则创建新文件。
  • "rb":只读模式,打开一个二进制文件用于输入。
  • "wb":只写模式,打开一个二进制文件用于输出,如果文件已存在则清空原有内容,如果文件不存在则创建新文件。
  • "ab":追加模式,打开一个二进制文件用于在文件尾部追加数据,如果文件不存在则创建新文件。
  • "r+":读写模式,打开一个文本文件用于读写,文件必须已存在。
  • "w+":读写模式,打开一个文本文件用于读写,如果文件已存在则清空原有内容,如果文件不存在则创建新文件。
  • "a+":读写模式,打开一个文本文件用于读写,文件不存在则创建新文件,所有写入操作都追加到文件尾部。
  • "rb+":读写模式,打开一个二进制文件用于读写,文件必须已存在。
  • "wb+":读写模式,打开一个二进制文件用于读写,如果文件已存在则清空原有内容,如果文件不存在则创建新文件。
  • "ab+":读写模式,打开一个二进制文件用于读写,文件不存在则创建新文件,所有写入操作都追加到文件尾部。

6.17 文件的读写操作

文件的读写操作可以通过一系列函数来完成,如 freadfwritefgetcfputcfgetsfputs 等。这些函数通常使用缓冲机制来提高性能。

6.18 文件定位

文件定位可以通过 fseek 函数来实现,该函数允许移动文件指针到文件中的任意位置。ftell 函数可以获取文件指针的当前位置。

fseek 函数参数说明:

  • stream:指向 FILE 结构的指针。
  • offset:偏移量,可以是正数或负数。
  • whence:定位基准点,可以是 SEEK_SET(文件开头)、SEEK_CUR(当前文件位置)或 SEEK_END(文件末尾)。

示例代码:

#include <stdio.h>
#include <errno.h>
#include <string.h>int main() {// 打开文件FILE *file = fopen("example.txt", "r+");if (file == NULL) {fprintf(stderr, "打开文件失败: %s\n", strerror(errno));return 1;}// 移动文件指针到文件开头if (fseek(file, 0, SEEK_SET) != 0) {fprintf(stderr, "移动文件指针失败: %s\n", strerror(errno));fclose(file);return 1;}// 获取文件指针的当前位置long position = ftell(file);if (position == -1) {fprintf(stderr, "获取文件指针位置失败: %s\n", strerror(errno));fclose(file);return 1;}printf("文件指针位置: %ld\n", position);// 移动文件指针到文件末尾if (fseek(file, 0, SEEK_END) != 0) {fprintf(stderr, "移动文件指针失败: %s\n", strerror(errno));fclose(file);return 1;}// 获取文件指针的当前位置position = ftell(file);if (position == -1) {fprintf(stderr, "获取文件指针位置失败: %s\n", strerror(errno));fclose(file);return 1;}printf("文件指针位置: %ld\n", position);fclose(file);return 0;
}

6.19 文件错误处理

在进行文件操作时,必须注意处理可能出现的错误。可以使用 ferrorclearerr 函数来帮助诊断和清除错误状态。

示例代码:

#include <stdio.h>
#include <errno.h>
#include <string.h>int main() {// 打开文件FILE *file = fopen("nonexistent.txt", "r");if (file == NULL) {fprintf(stderr, "打开文件失败: %s\n", strerror(errno));return 1;}char buffer[100];size_t bytes_read;// 读取文件bytes_read = fread(buffer, 1, sizeof(buffer), file);if (ferror(file)) {fprintf(stderr, "读取文件失败: %s\n", strerror(errno));fclose(file);return 1;}// 清除错误状态clearerr(file);fclose(file);return 0;
}

文件映射

文件映射是一种高效的数据处理方法,它将文件内容直接映射到进程的虚拟地址空间,使得对文件的操作就像对内存的操作一样简单。文件映射通常通过 mmap 函数来实现。

6.20 使用 mmap 进行文件映射

mmap 函数可以将文件或其他对象映射到内存,映射的内存区域可以直接被读写。

示例代码:

#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>int main() {// 打开文件int fd = open("largefile.dat", O_RDONLY);if (fd == -1) {perror("打开文件失败");return 1;}// 获取文件大小struct stat st;if (fstat(fd, &st) == -1) {perror("获取文件大小失败");close(fd);return 1;}// 映射文件到内存void *addr = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);if (addr == MAP_FAILED) {perror("映射文件失败");close(fd);return 1;}// 处理映射内存// ...// 解映射内存if (munmap(addr, st.st_size) == -1) {perror("解映射内存失败");close(fd);return 1;}// 关闭文件if (close(fd) == -1) {perror("关闭文件失败");return 1;}return 0;
}

总结

本文详细介绍了C语言文件操作中的库函数和系统调用,解释了它们的工作原理、区别和联系,并通过实际示例展示了如何使用这些函数。通过本文的学习,读者应能全面理解C语言文件操作的底层机制,为编写高效、可靠的程序提供有力支持。

希望本文能够帮助读者深入理解和应用C语言中的文件操作技术。如果您有任何进一步的问题或建议,请随时留言交流。

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

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

相关文章

linux上qt打包(二)

sudo apt install git 新建一个文件夹 名为xiazai&#xff0c; chmod -R 777 xiazai cd xiazai 并进入这个文件夹&#xff0c;然后clone git clone https://github.com/probonopd/linuxdeployqt.git 此处可能要fanQiang才能下 cd linuxdeployqt文件夹 下载平台需要的…

Windos中解决redis-server.exe闪退问题

一、闪退原因 &#xff08;一&#xff09;数据状态异常 数据不一致 在 Redis 运行过程中&#xff0c;如果发生意外情况&#xff0c;如突然断电、系统崩溃或者不正确的操作&#xff0c;可能会导致数据在内存中的存储状态不一致。例如&#xff0c;Redis 使用多种数据结构&#x…

【数据分享】2013-2023年我国省市县三级的逐年CO数据(免费获取\excel\shp格式)

空气质量数据是在我们日常研究中经常使用的数据&#xff01;之前我们给大家分享了2000-2023年的省市县三级的逐年PM2.5数据、2000-2023年的省市县三级的逐年PM10数据、2013-2023年的省市县三级的逐年SO2数据、2000-2023年省市县三级的逐年O3数据和2008-2023年我国省市县三级的逐…

华为WLAN基础配置(AC6005模拟配置)

AC6005基础配置 本次实验模拟华为AC6005的基本配置 Tip display interface GigabitEthernet 0/0/0 查看ap接口mac 前提条件&#xff1a;Vlan10为业务网段&#xff0c;vlan100为管理网段&#xff0c;5700作为dhcp。 5700配置如下 <Huawei>sy [Huawei]sys 5700 //设…

shell编程2 永久环境变量和字符串显位

声明 学习视频来自B站UP主 泷羽sec 常见变量 echo $HOME &#xff08;家目录 root用户&#xff09; /root cd /root windows的环境变量可以去设置里去新建 为什么输入ls dir的命令的时候就会输出相应的内容呢 因为这些命令都有相应的变量 which ls 通过这个命令查看ls命令脚本…

WebRTC服务质量(05)- 重传机制(02) NACK判断丢包

WebRTC服务质量&#xff08;01&#xff09;- Qos概述 WebRTC服务质量&#xff08;02&#xff09;- RTP协议 WebRTC服务质量&#xff08;03&#xff09;- RTCP协议 WebRTC服务质量&#xff08;04&#xff09;- 重传机制&#xff08;01) RTX NACK概述 WebRTC服务质量&#xff08;…

AI工具如何深刻改变我们的工作与生活

在当今这个科技日新月异的时代&#xff0c;人工智能&#xff08;AI&#xff09;已经从科幻小说中的概念变成了我们日常生活中不可或缺的一部分。从智能家居到自动驾驶汽车&#xff0c;从医疗诊断到金融服务&#xff0c;AI正以惊人的速度重塑着我们的世界。 一、工作方式的革新…

基于matlab的单目相机标定

链接&#xff1a; 单目相机标定&#xff08;使用Matlab&#xff09; 用Matlab对单目相机参数的标定步骤&#xff08;保姆级教程&#xff09; 1.准备代码 调用摄像头代码&#xff08;用于测试摄像头是否可用&#xff09;&#xff1a; #https://blog.csdn.net/qq_37759113/art…

[maven]使用spring

为了更好理解springboot&#xff0c;我们先通过学习spring了解其底层。 这里讲一下简单的maven使用spring框架入门使用。因为这一块的东西很多都需要联合起来后才好去细讲&#xff0c;本篇通过spring-context大致地介绍相关内容。 注意&#xff1a;spring只是一个框架&#xff…

eBay如何养号?新手养号宝典

​ebay是热门的跨境电商平台之一&#xff0c;然而与其他跨境电商平台不同&#xff0c;不同等级的ebay账户可刊登的数量是不同的。对于新手来说&#xff0c;想要提升ebay账户的等级就需要养号。那ebay如何养号&#xff1f;本文将带来一些实用的养号策略&#xff0c;帮助新手快速…

学习日志024--opencv中处理轮廓的函数

目录 前言​​​​​​​ 一、 梯度处理的sobel算子函数 功能 参数 返回值 代码演示 二、梯度处理拉普拉斯算子 功能 参数 返回值 代码演示 三、Canny算子 功能 参数 返回值 代码演示 四、findContours函数与drawContours函数 功能 参数 返回值 代码演示 …

梳理你的思路(从OOP到架构设计)_UML应用:业务内涵的分析抽象表达03

目录 1、举例(四)&#xff1a;五子棋 【五子棋】 的分析步骤 2、讨论&#xff1a; 模型与代码 1、举例(四)&#xff1a;五子棋 【五子棋】 的分析步骤 Step-1: 找到主角— 棋手&#xff0c;很容易发现核心的概念了&#xff0c;例如&#xff1a;五子棋游戏的主角是棋手(玩家…

人员离岗监测摄像机智能人员睡岗、逃岗监测 Python 语言结合 OpenCV

在安全生产领域&#xff0c;人员的在岗状态直接关系到生产流程的顺利进行和工作环境的安全稳定。人员离岗监测摄像机的出现&#xff0c;为智能人员睡岗、逃岗监测提供了高效精准的解决方案&#xff0c;而其中的核心技术如AI识别睡岗脱岗以及相关的算法盒子和常见的安全生产AI算…

【计算机网络】Layer4-Transport layer

目录 传输层协议How demultiplexing works in transport layer&#xff08;传输层如何进行分用&#xff09;分用&#xff08;Demultiplexing&#xff09;的定义&#xff1a;TCP/UDP段格式&#xff1a; UDPUDP的特点&#xff1a;UDP Format端口号Trivial File Transfer Protocol…

车牌识别OCR授权:助力国产化升级,全面提升道路监控效率

政策背景&#xff1a;国产化升级&#xff0c;推动道路监控产业转型 随着国家对信息安全的重视&#xff0c;国内各大公安、政企机构已进入全面升级国产化平台的实施阶段。根据最新的政策要求&#xff0c;公安和政府部门必须在未来三年内完成平台的国产化替换工作。这一举措不仅…

YOLOv5-7.0训练过程中出现报错Example: export GIT_PYTHON_REFRESH=quiet

出现报错&#xff1a; This initial message can be silenced or aggravated in the future by setting the $GIT_PYTHON_REFRESH environment variable. Use one of the following values: - quiet|q|silence|s|silent|none|n|0: for no message or exception - warn…

KALI安装操作及过程

以下是在计算机上安装 Kali Linux 的详细教程&#xff1a;&#xff08;通常我直接使用虚拟机&#xff09; 解压虚拟机安装包&#xff0c;直接在虚拟机中打开KALI &#xff08;将内存改为4GB&#xff09; 初始密码账号&#xff1a;kali 一、准备工作 下载 Kali Linux 镜像文件…

Linux环境安装Jenkins

Linux环境安装Jenkins Jenkins和JDK的版本 Jenkins和JDK的版本需要对应&#xff0c;不然无法正常启动。 Jenkins稳定版下载地址 Jenkins服务 手动使用命令启动和关闭Jenkins比较麻烦&#xff0c;所以可以把Jenkins设置成开机启动。 创建Jenkins.sh文件 JAVA_HOME和jenk…

ComfyUI 与 Stable Diffusion WebUI 的优缺点比较

ComfyUI与Stable Diffusion WebUI都是AI绘画领域比较知名两款产品&#xff0c;两者存在诸多差异&#xff0c;本篇就带你熟悉二者的优劣&#xff0c;方便自己做出决策。 界面与操作 ComfyUI&#xff1a;界面简洁直观&#xff0c;通过节点和连线的方式构建工作流&#xff0c;用…

2024年第十五届蓝桥杯青少组C++国赛—割点

割点 题目描述 一张棋盘由n行 m 列的网格矩阵组成&#xff0c;每个网格中最多放一颗棋子。当前棋盘上已有若干棋子。所有水平方向或竖直方向上相邻的棋子属于同一连通块。 现给定棋盘上所有棋子的位置&#xff0c;如果要使棋盘上出现两个及以上的棋子连通块&#xff0c;请问…