
用 fopen 打开文件,返回类型为 FILE *,其是一个结构体,包含
读写文件不是直接写入或读磁盘,而是先写入内存中的缓冲区,待缓冲区满了才放入磁盘中。
IO缓冲区的作用?
而将缓冲区的数据刷新到磁盘有三种方式
fflush 刷新缓冲区fclose、return(main函数)、exit (main函数)
C标准函数调用Linux系统的API,然后调用内核层设备驱动函数通过设备驱动操作硬件。

内核区存在PCB进程控制块,其包含文件描述符表,0~2是默认打开的,之后每打开一个新文件,则占用一个文件描述符,而且使用的是空间的最小的一个文件描述符。
char* argv[] 获取malloc 申请的空间就存放在这当然虚拟地址空间是4G并不代表程序实际使用了4G的内存空间,虚拟地址空间会映射到实际的内存空间中。
虚拟内存的存在主要有三个作用:


open 打开未存在的文件。#include
#include
#include
#include
#include
#include
int main()
{
int fd;
// 打开不存在的文件
fd = open("bucunz", O_RDWR);
if(fd == -1)
{
perror("open file");
exit(1);
}
// 关闭文件
int ret = close(fd);
printf("ret = %d\n", ret);
if(ret == -1)
{
perror("close file");
exit(1);
}
}
这里 open 的第一个参数表示要打开的文件,O_RDWR 表示可读可写,其他还有 O_RDONLY 只读打开以及 O_WRONLY 只写打开,其返回文件描述符,如果出现错误则返回 -1,这里使用 perror 在错误输出之前输出里面的内容。关闭文件使用 close 其参数为文件描述符,如果产生错误返回 -1,成功则返回 0。

#include
#include
#include
#include
#include
#include
int main()
{
int fd;
// 创建新文件
fd = open("myhello", O_RDWR | O_CREAT, 0777);
if(fd == -1)
{
perror("open file");
exit(1);
}
printf("fd = %d\n", fd);
// 关闭文件
int ret = close(fd);
printf("ret = %d\n", ret);
if(ret == -1)
{
perror("close file");
exit(1);
}
}
这里通过在 open 中添加参数 O_CREAT 以在没有该文件时生成新文件。

可以看到其 fd 为 3,前面 0~2 为默认打开的,所以从 3 开始分配。

我们之前设置最后参数为 0777 表示赋予文件所有用户所有权限,但我们看到该文件的其他用户并没有写权限,这是因为Linux在赋予文件全选时需要减去一个掩码,可以通过 umask 显示,这里为0002,其取反与赋予的权限通过二进制与操纵产生最终的文件权限。
创建新文件,如果文件已存在则出错返回
可以在 open 参数添加 O_EXCL实现,即fd = open("myhello", O_RDWR | O_CREAT | O_EXCL, 0777);
如果文件存在,将长度截断为0字节
可以在 open 参数添加 O_TRUNC 实现,即fd = open("myhello", O_RDWR | O_TRUNC);
read 的函数原型如下:

其返回值有三种可能:
write 的函数原型如下:

如果写成功,返回写入的字节数,如果出错则返回 -1。
read 与 write 函数均需要指定读取或写入文件的文件描述符,缓冲区,以及读取和写入的字节数。
下面使用 open,read,write 实现从一个文件读取数据放入另一个文件中:
#include
#include
#include
#include
#include
#include
int main(void)
{
int fd = open("test1.txt", O_RDONLY);
if(fd == -1)
{
perror("open file1");
exit(1);
}
int fd2 = open("test2.txt", O_WRONLY | O_CREAT, 0777);
if(fd2 == -1)
{
perror("open file2");
exit(1);
}
int size;
char buffer[8]; // 缓冲区
while((size=read(fd, buffer, 8)) != 0)
{
if(size == -1)
{
perror("read file1");
exit(1);
}
int ret = write(fd2, buffer, size);
if(ret == -1)
{
perror("write file2");
exit(1);
}
printf("%d\n", ret);
}
if(close(fd) == -1)
{
perror("close file1");
exit(1);
}
if(close(fd2) == -1)
{
perror("close file2");
exit(1);
}
return 0;
}

可以看到 test1.txt 一共50个字节,前六次每次读取缓冲区大小的字节数(8),最后一次读取最后2个字节。
lseek 函数原型如下:

lseek 重新定位文件偏移,offset 表示偏移量,whence 表示偏移位置,即从哪开始偏移。
whence 共有三种选项:
SEEK_SET 文件偏移被设为 offset,即从文件头开始偏移 offset 字节SEEK_CUR 文件偏移被设为文件指针当前位置+offset偏移字节数,即从当前位置开始偏移offset字节SEEK_END 文件偏移被设为文件的大小+offset偏移字节数,即从文件尾开始偏移offset字节这里需要说明的是,当从文件尾开始偏移offset字节时,这时并不会改变文件大小,如果此时在该位置进行写入,那么中间部分会变成空洞,进行占位,除非数据被写入这些空洞,否则返回 \0。空洞的作用有一点就是当我们下载文件时,用空洞进行占位可以需要的空间保留下来。
如果成功返回从文件头开始的偏移字节数,如果产生错误,返回 -1。
#include
#include
#include
#include
#include
#include
int main(void)
{
int fd = open("./test1.txt",O_RDWR);
if( fd == -1)
{
perror("open test1.txt:");
exit(1);
}
int ret = lseek(fd,0,SEEK_END);
printf("file length = %d\n",ret);
// 文件扩展
ret = lseek(fd,2000,SEEK_END);
printf("return value = %d\n",ret);
// 实现文件拓展,需要最后一次写操作
write(fd,"a",1);
close(fd);
return 0;
}


可以看到 test1.txt 被扩展为 2051 字节了,且中间有很多空洞 ^@