目录
C文件接口
1、fopen
path:带有路径的文件名称,不带路径,则默认当前路径
mode:打开文件的方式
返回值:成功返回文件流指针FILE*,失败返回NULL
mode打开方式的方式:
r:只读方式打开,将文件指针指向文件头,如果文件不存在,则返回空。
r+:读写方式打开,将文件指针指向文件头,如果文件不存在,则返回空。
w:写入方式打开,将文件指针指向文件头并将文件内容清空。如果文件不存在则创建。
w+:读写方式打开,将文件指针指向文件头并将文件内容清空,如果不存在则创建。
a:写入方式打开,将文件指针指向文件末尾。如果文件不存在则创建。
a+:读写方式打开,将文件指针指向文件末尾。如果文件不存在则创建。
2、fwrite
ptr:向文件中写入什么内容
size:写入多少字节
nmemb:期望写多少块(就是多少个size)
stream:文件流指针
返回值:返回成功写入的块数
3、fread
ptr:从文件中读到的内容保存到ptr指向的空间中
size:读多少字节
nmemb:读多少块(就是多少个size)
stream:文件流指针
返回值:返回成功读入的个数
4、fseek
作用:移动文件流指针的位置
stream:文件流指针
offset:偏移量
whence:将文件流指针偏移到什么位置
SEEK_SET:文件头部
SEEK_CUR:文件流指针的位置
SEEK_ENO:文件末尾
返回值:成功返回0,失败返回-1。
5、代码验证
errno:系统当中的一个错误码;当我们调用一个函数出错了,则会给errno赋值成为对应的错误码(整形)。
perror:会直接去拿errno当中的值进行解析,解析完毕后,进行打印。
#include<stdio.h>
2 #include<string.h>
3 int main(){
4 FILE* fp = fopen("1.txt","w+");
5 if(fp == NULL){
6 perror("fopen");
7 return 0;
8 }
9 //测试写
10 const char* ptr = "hello world";
11 printf("sucess\n");
12 size_t wsize= fwrite(ptr,1,strlen(ptr),fp);
13 printf("%ld\n",wsize);
14 //测试读
15 fseek(fp, 3, SEEK_SET);
16 char buf[1024] = {0};
17 size_t r_size = fread(buf, 1, sizeof(buf) - 1, fp);
18 printf("r_size = %ld\n", r_size);
19 printf("buf = %s\n", buf);
20 //如果不关闭文件流指针,则会造成文件句柄泄露
21 fclose(fp);
22 return 0;
23 }
24
系统调用文件接口
1、open
pathname:要打开或者创建的目标文件
flags:打开方式
①O_RDONLY:只读打开
②O_WRONLY:只写打开
③O_RDWR:读写打开
注意:这三个常量,必须指定且指定一个,用下面一个或者多个常量进行“或运算”构成flags
O_CREAT:若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限。
O_APPEND:追加写
mode:指定新创建文件的权限
返回值:成功返回新文件的文件描述符,失败返回-1
2、write
fd:文件描述符
buf:将buf指向的内容写入到文件中
count:期望写多少字节
返回值:返回写入的字节数量
3、read
fd:文件描述符
buf:从文件中读到的内容写入buf指向的空间去。
count:期望都多少字节
返回值:返回读到的字节的数量
4、lseek
fd:文件描述符
offset:偏移量,单位字节
whence:将文件流指针偏移到什么位置
SEEK_SET:文件头部
SEEK_CUR:当前文件流指针的位置
SEEK_END:文件末尾
返回值:成功返回0,失败返回-1
5、代码验证
#include<unistd.h>
#include<stdio.h>
#include<string.h>
int main(){
int fd = open("1.txt",O_RDWR | O_CREAT,0664);
if(fd < 0){
perror("open");
return 0;
}
printf("open sucess\n");
//测试写
const char* buf = "Linux is so easy";
ssize_t fsize = write(fd,buf,strlen(buf));
printf("fsize = %ld\n",fsize);
//测试读
lseek(fd,0,SEEK_SET);
char temp[1024] = {0};
ssize_t rd_size = read(fd,temp,sizeof(temp) - 1);
printf("rd_size = %ld\n",rd_size);
close(fd);
return 0;
}
注意:这里read这里传的是sizeof(temp) - 1;在字符数组中预留了\0的位置,防止后续在访问的时候越界访问导致崩溃。
文件描述符
值和含义:
Linux进程默认情况下会有3个打开的文件描述符,分别是标准输入0,标准输出1,标准错误2。它们对应的物理设备一般是:键盘,显示器,显示器。
int main(){
6 int fd = open("1.txt",O_RDWR | O_CREAT,0664);
7 if(fd < 0){
8 perror("open");
9 return 0;
10 }
11 printf("%d\n",fd);
12 while(1){
13
14 }
可以看到拿到的文件描述符是3,这是一个正数。 可以通过/proc/[pid]/fd查看文件夹下的文件描述符信息。
其中的软连接文件的名字,正好对应文件描述符的值。站在操作系统内核的角度上来理解:
文件描述符就是从0开始的整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件。于是就有了file结构体,表示一个已经打开的文件对象。而进程执行open系统调用,所以必须让文件和进程关联起来。每个进程都有一个指针*file,指向一张表files_struct,该表最重要的部分就是包含一个指针数组,每一个元素都是一个指向打开文件的指针!所以,本质上,文件描述方法就是该数组的下标。所以,只要拿着文件描述符,就可以找到对应的文件。
文件描述符和文件流指针的关系
文件流指针指向的结构体中保存了文件描述符;文件流指针对应的结构体struct _IO_FILE这个结构体内部的成员变量int _fileno保存了对应的文件描述符的数值。
int main(){
4 FILE* fp = fopen("1.txt","w+");
5 if(fp == NULL){
6 perror("fopen");
7 return 0;
8 }
9 printf("%d\n",fp->_fileno);
重定向
1、重定向符号
>:清空重定向
>>:追加重定向
2、重定向的接口
作用:将newfd的值重定向为oldfd,即newfd拷贝oldfd
参数:oldfd/newfd均为文件描述符
成功:关闭newfd;让newfd指向oldfd对应的struct file*结构体
失败:如果oldfd是一个非法/无效的文件描述符,则重定向失败,newfd没有变化;如果oldfd和newfd的值相等,则什么事都不干。
3、内核角度理解重定向
看如下代码:
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include <sys/types.h>
int main(){
int fd = open("myfile",O_RDWR | O_CREAT,0664);
if(fd < 0){
perror("open");
return -1;
}
dup2(fd,1);
printf("hello\n");
fflush(stdout);
while(1){
}
}
可以看到本该打印到屏幕的内容去到了myfile里面
现在来查看文件描述符的信息:
可以看出本该指向标准输出的指针指向了myfile文件。
文件描述符是一个file结构体里面的一个指针数组的下标,重定向就是改变了其中指针的指向。(使本该指向标准输出的指针指向了myfile文件)
动态库和静态库
1、什么是库
静态库和动态库都是程序代码(二进制文件)的集合,一般为了方便将程序提供给第三方使用,就是将程序编写为库文件提供给第三方使用。
好处:不会泄露公司的源码;调用者不必关心内部实现,只需要关心如何使用即可。
静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库。
动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
2、动态库
特征:
windows:没有前缀,后缀为dll
linux:前缀为lib,后缀为.so
生成:
使用gcc/g++编译器,增加两个命令行参数:
-fPIC: 产生位置无关码
-shared:表示生成共享库格式
库名格式:libxxx.so
使用动态库:
编译可执行程序的时候,依赖动态库:
-L [path]:指定动态库所在的路径
-l[动态库的名称(去掉前缀和后缀之后的名称)]
可以看到这里虽然编译通过,但是却没有运行成功。可以使用ldd命令查看程序依赖的库:
原因就是运行的时候找不到库文件的位置。 下面的三种方法可以让程序找到动态库:
1、将动态库放到可执行程序的路径下(不推荐)
2、在~/.bashrc文件下配置LD_ LIBRARY_PATH:动态库的环境变量(推荐)
3、放到系统库的路径下:/lib64(极力不推荐)
3、静态库
特征:
windows:没有前缀,后缀为.lib
linux:前缀为lib,后缀为.a
生成:
第一阶段:使用gcc/g++将源代码编译成为目标程序(.o)
第二阶段:使用ar -rc命令编译目标程序为静态库
ar -rc [静态库文件名称] [目标程序]
注意:不能直接用源代码编译
查看静态库:
使用
如果在编译可执行程序时候,使用到了静态库,则静态库会被编译到可执行程序当中去。并没有可执行程序以来动态库的问题
软硬链接
软链接
软链接:目标文件的快捷方式
生成 :ln -s 源文件 软链接文件
注意事项:
1、修改软链接文件,源文件也会被修改
2、源文件如果被删除,软链接文件还在的,修改软链接文件,会重新建立源文件,重新建立链接关系(这种要慎重);一定在删除源文件的时候,将软链接文件也删除(以防后患)
硬链接
硬链接:目标文件的替身
生成:ln 源文件 硬链接文件
硬链接文件和源文件拥有相同的节点号。多个文件引用用一个节点的时候,引用计数会++;当文件删除的时候,引用计数会减减,直到引用计数减到0,才会释放inode节点。