🌟 各位看官好,我是maomi_9526!
🌍 种一棵树最好是十年前,其次是现在!
🚀 今天来学习C语言的相关知识。
👍 如果觉得这篇文章有帮助,欢迎您一键三连,分享给更多人哦
目录
1. 文件的概念
狭义理解:
文件是存储在磁盘上的永久性数据,通过磁盘进行I/O操作。
广义理解:
在Linux中,几乎一切都可以看作是文件(包括硬件设备如键盘、显示器、磁盘、进程等)。
文件操作:
文件包括文件的元数据和内容,所有操作可以分为文件内容操作和文件属性操作。
文件操作并非是通过C/C++语言库函数进行实现的,而是通过调用文件相关的系统的接口进行实现的的。
2. C语言文件操作接口
打开文件:使用
fopen()
来打开文件,可以选择不同的文件模式(如r
,w
,a
等)。
写入文件:使用
fwrite()
向文件写入数据,fclose()
用于关闭文件。读取文件:使用
fread()
从文件中读取数据,feof()
用于检测文件末尾。标准输出:可以使用
printf()
、fwrite()
或fprintf()
向标准输出流(stdout
)输出数据。
#include<stdio.h>
int main()
{
FILE* fp=fopen("file","W");
if(!fp)
{
printf("fopen error\");
}
while(1);
fclose(fp);
return 0;
}
简单回顾程序执行:
- cwd:指向当前进程运行目录的一个符号链接。
- exe:指向启动当前进程的可执行文件(完整路径)的符号链接。
- 打开文件,本质是进程打开,所以,进程知道自己在哪里,即便文件不带路径,进程也知道。由此OS就能知道要创建的文件放在哪里。
3. 系统调用与文件I/O
打开文件的方式不仅仅是fopen,ifstream等流式,语言层的方案,其实系统才是打开文件最底层的方案。不过,在学习系统文件IO之前,先要了解下如何给函数传递标志位,该方法在系统文件IO接口中会使用到标志位
3.1标志位传递方法
#include<stdio.h>
#define ONE_FALGE 1<<0
#define TWO_FALGE 1<<1
#define THR_FALGE 1<<2
void Print(int falge)
{
if(falge & ONE_FALGE)
{
printf("ONE_FALGE");
}
if(falge& TWO_FALGE)
{
printf("TWO_FALGE");
}
if(falge&THR_FALGE)
{
printf("THR_FALGE");
}
}
int main()
{
Print(ONE_FALGE|TWO_FALGE);
printf("\n");
Print(ONE_FALGE|TWO_FALGE);
printf("\n");
Print(ONE_FALGE|TWO_FALGE|THR_FALGE);
printf("\n");
}
3.2系统调用
除了C语言的标准库函数,文件操作也可以通过系统调用(如 open()
, write()
, read()
)直接与操作系统交互。这些系统调用是与内核直接交互的底层接口。
3.2.1open
常用标志位:
O_RDONLY:以只读方式打开文件。
O_WRONLY:以只写方式打开文件。
O_RDWR:以读写方式打开文件。
O_CREAT:如果文件不存在,则创建该文件。
O_APPEND :以追加模式打开文件。
O_TRUNC:如果文件已经存在,并且以写模式打开,则将文件的大小截断为零,丢弃文件的内容。
3.2.2权限掩码(umask)
#include<stdio.h>
#include<sys/types.h>
#include<fcntl.h>
int main()
{
int id=open("file",O_WRONLY|O_CREAT|O_APPEND,666);
if(id<0)
{
perror("open\n");
}
return 0;
}
为什么会出现这样的情况?(我们设置文件权限为666但是实际权限只有664)
权限掩码:作用是控制文件和目录的访问权限,确保系统的安全性和资源的正确使用。在操作系统中,文件和目录通常会有不同的用户角色(如文件所有者、同一组用户和其他用户),每个角色可以对文件或目录执行不同的操作。权限掩码通过设置这些权限来定义哪些用户可以执行哪些操作。
文件掩码的权限会在创建文件时被隐藏。
在程序中定义权限掩码:
umask函数
将权限掩码设置为0:
#include<stdio.h>
#include<sys/types.h>
#include<fcntl.h>
#include<sys/stat.h>
int main()
{
umask(0);
int id=open("file",O_WRONLY|O_CREAT|O_APPEND,666);
if(id<0)
{
perror("open\n");
}
return 0;
}
3.3文件描述符
通过 open()
返回的文件描述符(一个小整数)管理打开的文件。每个进程有自己的文件描述符表。
此时的open返回值就是file文件的描述符:
3.3.1操作系统
通过这张表我们可以清楚认识到,所谓的C/C++中的文件操作的函数本质上都是对系统接口函数的封装。
3.3.2默认文件描述符0&1&2
#include<stdio.h>
#include<sys/types.h>
#include<fcntl.h>
#include<sys/stat.h>
int main()
{
umask(0);
int id1=open("file1",O_WRONLY|O_CREAT|O_APPEND,0666);
int id2=open("file2",O_WRONLY|O_CREAT|O_APPEND,0666);
int id3=open("file3",O_WRONLY|O_CREAT|O_APPEND,0666);
printf("%d\n",id1);
printf("%d\n",id2);
printf("%d\n",id3);
return 0;
}
Linux默认会打开三个文件描述符分别是0,1,2
- 0:标准输入 1:标准输出 2:标准错误
- 他们对应的物理设备分别为:键盘,显示器,显示器
3.3.3文件描述符的分配规则
文件描述符的分配规则:在files_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。
操作: | 默认打开0,1,2 | 关闭文件描述符:0 |
打开的文件描述符: | 3,4,5 | 0,3,4 |
结论: | 在files_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。 |
3.4重定向
通过关闭标准输出 (stdout
,文件描述符 1) 并将其指向文件,可以实现输出重定向,常见的有 >
(输出重定向)和 <
(输入重定向)。
重定向简单原理:
#include<stdio.h>
#include<sys/types.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<stdlib.h>
int main()
{
close(1);
int id1=open("file1",O_WRONLY|O_CREAT|O_APPEND,0666);
int id2=open("file2",O_WRONLY|O_CREAT|O_APPEND,0666);
int id3=open("file3",O_WRONLY|O_CREAT|O_APPEND,0666);
printf("%d\n",id1);
printf("%d\n",id2);
printf("%d\n",id3);
return 0;
}
当默认的标准输出流被关闭时,file1
会占据文件描述符 1(标准输出)。此时,printf
使用文件描述符 1 来确定输出目标,因此其输出会被写入到 file1
中。
解释: 重定向的原理是通过修改文件描述符的指向,使其指向新的文件或设备,从而改变程序的输出路径。