Linux 文件
1.操作系统的文件函数
我们以一个操作系统的文件函数为例 其它是差不多的
open函数

const char *pathname(必选)
作用:指定要打开 / 创建的文件路径(绝对路径或相对路径)。
关键说明: 支持绝对路径(如 /home/user/test.txt)和相对路径(如 test.txt、data/log.csv);
若路径是符号链接,默认会跟随链接指向的真实文件(可通过 O_NOFOLLOW 标志禁用此行为);
路径长度不能超过系统限制(Linux 中默认最大路径长度为 PATH_MAX,通常是 4096 字节)。
2. int flags(必选,核心控制位)
作用:通过「标志位组合」指定文件的访问模式、创建 / 修改行为、IO 特性等,是系统调用 open() 最关键的参数。
标志位分类(需用按位或 | 组合): 标志位是预定义的宏(如 O_RDONLY),本质是整数,不同标志位的二进制位互不冲突,因此可用 | 组合多个行为。
访问模式标志(必选,且互斥,只能选一个) 控制文件的读写权限,是 flags 中必须包含的核心标志:

open 函数的 flag 参数由一系列宏组成,每个宏对应一个独立的二进制位。
例如:
O_RDONLY:二进制 000...0001(最低位为 1),表示只读。
O_WRONLY:二进制 000...0010(第二位为 1),表示只写。
O_RDWR:二进制 000...0100(第三位为 1),表示读写。
O_CREAT:二进制 000...10000000(第 8 位为 1),表示创建文件。
O_APPEND:二进制 000...100000000(第 9 位为 1),表示追加写入。
3.mode_t mode(可选,仅 O_CREAT/O_TMPFILE 时有效)
作用:指定「新建文件」的权限位(文件所有者、组用户、其他用户的读 / 写 / 执行权限)。
关键说明: 仅当 flags 包含 O_CREAT(创建普通文件)或 O_TMPFILE(创建临时文件)时,该参数才生效;
否则会被忽略; mode 是八进制整数(必须以 0 开头,如 0644,而非 644),对应文件权限的 rwx 组合(r=4、w=2、x=1);
实际文件权限 = mode & ~umask:系统会用「默认掩码 umask」过滤 mode 的权限位(umask 默认为 0022,即禁用组用户和其他用户的写权限)。
常用 mode 取值(八进制):
我们可以写几个代码试试
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>int main()
{umask(0);int fd = open("log.txt", O_WRONLY|O_CREAT|O_APPEND, 0666);if(fd < 0){printf("open file error\n");return 1;}printf("fd: %d\n", fd);const char *message = "xxx";write(fd, message, strlen(message));close(fd);return 0;
}

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>int main()
{umask(0);int fd = open("log.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666);if(fd < 0){printf("open file error\n");return 1;}printf("fd: %d\n", fd);const char *message = "xxx\n";write(fd, message, strlen(message));close(fd);return 0;
}

2.open的返回值
open的返回值本质上是数组的下标
open 的返回值(文件描述符 FD),本质是 struct files_struct 所关联的「文件描述符表」数组的下标—— 更具体地说,是该结构中 fd_array(默认静态数组)或动态扩展的 fdtable->fd 数组的下标。
fd_array存放的是指针 每个指针 指向的是文件
我们可以来测试一下
int main()
{umask(0);int fd1 = open("log1.txt", O_WRONLY|O_CREAT|O_APPEND, 0666);int fd2 = open("log2.txt", O_WRONLY|O_CREAT|O_APPEND, 0666);int fd3 = open("log3.txt", O_WRONLY|O_CREAT|O_APPEND, 0666);int fd4 = open("log4.txt", O_WRONLY|O_CREAT|O_APPEND, 0666);if(fd1 < 0){printf("open file error\n");return 1;}printf("fd1: %d\n", fd1);printf("fd2: %d\n", fd2);printf("fd3: %d\n", fd3);printf("fd4: %d\n", fd4);return 0;
}

我们发现当我们open四个文件的时候
返回值是从3开始依次递增的
那么 数组0 1 2 的下标是什么
0 1 2是我们系统的三个流 一般是默认打开的
(这是操作系统的特性 并不是C语言的特性)
标准输入流 stdin: 键盘文件
标准输出流 stdout: 显示器文件
标准错误输出流 stderr: 显示器文件
分别是标准输入流 标准输出流 以及标准错误输出流

我们可以通过下面例子进行验证
#include <sys/stat.h>
#include <string.h>
#include <unistd.h>int main()
{const char *msg = "hello Linux\n";write(1, msg, strlen(msg));write(2, msg, strlen(msg));return 0;
}

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>int main()
{char buffer[1024];ssize_t s = read(0, buffer, sizeof(buffer));if(s < 0) return 1;buffer[s] = '\0';//read只按字节读取不管你是不是字符串有没有\0printf("echo: %s\n", buffer);return 0;
}

strlen 的作用是 计算字符串的 “有效字符数”,它从字符串起始位置开始计数,直到遇到 '\0' 为止,不包含 '\0' 本身。
对于操作系统而言它不知\0是字符串结束的标志 它只会把\0当作一个普通字节读入
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>int main()
{umask(0);int fd = open("log.txt", O_WRONLY|O_CREAT|O_APPEND, 0666);if(fd < 0){printf("open file error\n");return 1;}printf("fd: %d\n", fd);const char *message = "xxx";write(fd, message, strlen(message)); // +1?close(fd);return 0;
}
write(read等操作系统调用的文件相关函数同理) 是系统调用,核心作用是 “把用户指定的‘有效数据’原封不动地写入文件 / 设备”,它完全不认识 '\0'
—— 对 write 来说,'\0' 只是一个普通的二进制字节(ASCII 码 0),不是 “结束标志”。
你的需求是把 message 的内容("xxx")写入文件 log.txt,那么: 用 strlen(message)(3):write 只写 3 个有效字符 'x'、'x'、'x',文件里的内容就是 xxx(正确);
用 strlen(message)+1(4):write 会写 4 个字节 'x'、'x'、'x'、'\0',文件里会多一个隐藏的 '\0' 字节(多余且无意义)。
当然我们也可以关闭其中某一个
#include <stdio.h>
#include <unistd.h> // 提供 close 函数的声明int main()
{close(1); // 关闭 stdout 对应的文件描述符(fd=1)int n = printf("stdin->fd: %d\n", stdin->_fileno);printf("stdout->fd: %d\n", stdout->_fileno);printf("stderr->fd: %d\n", stderr->_fileno);fprintf(stderr, "printf ret: %d\n", n);return 0;
}

close本质和C++智能指针类似
close的时候
引用计数--
再把指针置空
引用计数--后判断 如果为0系统回收 如果不为0(可能还有其他进程在用) 系统就停止操作 不回收
如果为0 系统就回收这个文件
3.操作系统文件函数和c语言文件函数的区别
C 语言的 fprintf 等文件函数是对操作系统 write、open 等系统调用的上层封装,它通过添加缓冲、格式化、跨平台适配等逻辑,让开发者能更简单地进行文件操作;
而系统调用则是更底层、更直接的内核接口,适合需要精细控制的场景。这种分层设计既保证了易用性,又保留了底层灵活性。
但是我们知道操作系统文件函数的返回值类型是int
而c语言文件函数的返回值类型是FILE*
FILE* 类型(C 标准库): 它是C 标准库定义的一个结构体指针(你可以理解为一个 “包装器”)。
这个结构体里包含了文件描述符(即 open 返回的 int),还额外添加了缓冲区、读写位置、错误标志、EOF 标志等用户态的管理信息。
它的作用是在系统调用之上封装一层 “易用接口”,让开发者无需直接处理底层细节(比如缓冲、跨平台兼容)。
