标准IO
打开文件:FILE* fopen(文件名字符串,打开模式)
r w a r+ w+ a+
(文本文件,二进制文件)
关闭文件: fclose(FILE* p);
读一个字符: int fgetc(FILE* P);
写一个字符: fputc(int c,FILE* P)
读一个字符串:fgets(char* buf,int size,FILE*)
写一个字符串:fputs(char* p,FILE* P);
char arr[4] = “abc” char* p = “abc”;(内存四区背完— 姜洋)
二进制读:fread(void* buf,每个块的大小,块个数,文件指针);
二进制写: fwrite(void*buf,每个块的大小,块个数,文件指针)
格式化读: int fscanf(FILE *stream, const char *format, …); 类似scanf
格式化写: int fprintf(FILE *stream, const char *format, …); 类似printf
文件偏移: fseek,rewind, ftell
(calloc,realloc) malloc -->memset() bzero( )
POSIX:可移植操作系统接口,软件需要在linux下运行,API关联的标准的总称
1> 文件操作系统调用: 打开文件,关闭文件,读写
2> 进程控制系统调用: 创建进程 关闭进程 ps -aux
3> 通信类控制系统调用: 发送消息,接受消息 socket套接字
4> 设备管理系统调用: 打开设备 关闭设备
5> 信息维护类系统嗲用:用户程序和操作系统之间的信息
文件:一组相关数据的集合, 文件名:集合的名称
普通文件:touch 文件
目录文件: mkdir 目录名
设备文件: 放在dev目录下,和普通文件一样
字符文件: sudo mknod 字符设备名 c 主编号 次编号
块 设 备: sudo mknod 字符设备名 b 主编号 次编号
管道设备: sudo mknod 管道设备名 p 主编号 次编号
网络设备: sudo mknod 网络设备名 s 主编号 次编号
链接文件: 理解成快捷方式
软连接:ln -s 被连接文件名 新文件名 ( 是保存了被连接文件的绝对路径,是另外一个新的文件,访问时用被连接文件的路径替换)
硬链接:ln 被连接文件名 新文件名 (普通文件没区别,指向硬盘的同一块区域)快捷方式
删除对应的被链接文件,软连接会受到影响,而硬链接不会。
typedef struct _iobuf{
char* ptr ; //下一个字符的位置
int cnt //剩余字节数
int* base; // 缓冲区的位置
int fd ; // 文件描述符 (文件的标志)
int flag ; //文件的访问模式
} FILE
文件描述符:是一个非负整数,是一个索引指向内核为每个进程所维护打开文件的记录
三个默认的打开的流: stdin(0)输入流 stdout(1)输出流 stderr(2)标准错误流
#include
函数原型:FILE *fdopen(int fd, const char *mode);
函数作用:把fd的文件描述符转换成文件指针,当文件IO中打开文件,需要用标准IO中的函数时需要使用。
函数参数:fd 文件描述符 mode打开模式
返 回 值:文件指针。
练习:找到标准输入的 文件指针。
#include
#include
#include
#include
int main()
{
FILE * file = fopen("abc","r");
FILE* p = fdopen(0,"r");
if(p)
{
printf("p = %p\n",p);
}
if(file == NULL)
{
//perror("fdopen");
printf("%s\n",strerror(errno));
exit(0);
}
return 0;
}
函数原型: int fileno(FILE *stream);
函数作用:文件指针转换成文件描述符。
函数参数:stream 是文件指针
返 回 值:文件描述符。
使用: 标准io打开的文件fopen,用read去读取或者write写需要文件描述符,需要进行转换。
#include
#include
#include
函数原型 int open(char *pathname, int flags, mode_t mode);
函数参数:pathname 文件名或者文件路径
flags 打开模式
mode 只有创建时候使用 文件创建权限0666
作 用: 打开文件
返 回 值: 文件描述符,也是文件的打开标志,失败返回-1;
打开模式:
第一组: 只读打开 O_RDONLY
只写方式:O_WRONLY
读写方式:O_RDWR
第二组: O_CREAT 创建文件,如果文件不存在则创建,需要和mode一起配合使用,mode的创建时候的文件权限
O_APPEND 追加的方式写入文件,如果文件已经有内容了,则在文件末尾添加内容,不会覆盖,文件末尾开始读不到内容。
O_TRUNC 覆盖写,每一次写都会把原有内容 清空。
函数原型: int close(int fd);
头文件: #include
作用: 关闭文件
返回值: 是否成功,如果是0则关闭成功 -1关闭失败
练习:创建danny.log文件 提示3秒后关闭
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
int fd = open("danny.log",O_RDWR| O_CREAT,0666);
if(fd == -1)
{
printf("%s\n",strerror(errno));
return 0;
}
sleep(1);
printf("倒计时1妙\n");
sleep(2);
close(fd);
}
#include
函数原型: ssize_t read(int fd, void *buf, size_t count);
函数参数: fd 文件描述符
buf 存放读取内容的缓存。(数组,malloc申请空间)
count:从文件中读取的字节数
返回值:如果成功返回实际读取的字节数 如果失败返回-1,文件末尾返回0;
函数原型: int write(int fd, const void *buf, long count);
头 文 件: #include
作 用: 写入文件内
参 数: fd 文件描述符
buf 存放读取内容的缓存。(数组,malloc申请空间)
count:从文件中读取的字节数
返回值:如果成功返回实际写入的字节数
失败返回-1 错误信息错入errno
练习:文件拷贝功能:./cp 文件1 文件2 (实现cp命令)
#include
#include
函数原型:off_t lseek(int fd, off_t offset, int whence);
作 用: 移动光标,移动文件偏移位置。
参 数:whence SEEK_CUR 从当前位置为餐卡偏移
SEEK_SET 从文件头开始偏移
SEEK_END 从文件尾开始偏移
offset:偏移多少,正数或者负数,正数往右,负数往
注 意: socket文件--网络设备文件,管道文件没法移动,
函数原型: int access(const char *pathname, int mode);
头 文 件: #include
参 数: *pathname,文件名 ,mode 用来判断某一个模式
mode格式如下:
R_OK 判断文件是否有可读权限
W_OK 可写权限
F_OK 文件是否存在
X_OK 判断文件是否可执行
返回值:如果是0则表示成功。失败返回-1 错误信息存入errno中
练习:判断参数中的文件是否存在,如果存在判断有可执行权限
函数原型:int remove(const char *pathname);
头文件: stdio.h
作用: 删除文件
返回值:如果成功返回0 失败返回-1 errno
如果pathnam是目录,则需要调用rmdir()处理。
如果pathname是文件,则会调用unlink()处理
函数原型: int fcntl(int fd, int cmd, ... /* arg */ );
头 文 件: #include #include
参 数: fd 文件描述符
cmd 命令
F_DUPFD 赋值文件描述符
F_GETFD 获取文件描述符标志
F_SETFD 修改文件描述符
F_GETFL 获取文件的状态标志
F_SETFL 设置文件的状态标志
F_GETLK 获取文件锁
F_SETLK 设置文件锁
F_GETOWN 获取信号处理
F_SETOWN 设置信号处理
第三个:操作命令的参数,比如说设置文件描述符,设置成多少需要用第三个
返回值:如果是错误 返回-1
返回文件描述符 返回文件锁 返回状态标志
第一个重点: 文件状态获取和设置(可读可写)
获取状态标志:通过第二个参数 F_GETFL 来获取到现有的状态标志。例如O_REDONLY ,O_WRONLY, O_RDWR,然后用int变量接受,则得到的属性存入到了int中,
把得到的返回值和某一个属性例如O_REONLY进行按位与操作
增加状态标志: 通过第二个参数F_SETFL来设置,第三个参数是设置的内容,我们可以通过 返回值 |= 你想为期增设的标志,最后调用fcntl函数保存标志。
删除状态标志: 通过第二个参数F_SETFL来设置,第三个参数是设置的内容,我们可以通过 返回值 &=你想为期增设的标志的~来设置,最后调用fcntl函数保存标志。
第二个重点 :设置IO设备非堵塞 F_SETFL
设置: O_NONBLOCK 属性。
第一种设置方式: 在open的时候添加非堵塞属性
第二种设置方式:使用fcntl函数添加非堵塞属性。
第三个重点:设置文件锁 F_SETLK F_GETLK
对文件枷锁后,只允许一个进程读写,其他进程等待读写,直到解锁。
struct flock {
...
short l_type; /* 锁的类型: F_RDLCK, F_WRLCK, F_UNLCK */
short l_whence; /*偏移量的起始位置,枷锁的参考位置 SEEK_SET, SEEK_CUR, SEEK_END */
off_t l_start; /* 正数表示往后偏移后枷锁, */
off_t l_len; /* 枷锁多少个字节 */ 如果字节数为0默认为偏移位置到文件结束。
pid_t l_pid; /* 锁的进程id号
...
};
代码:先定义机构体,给结构体成员赋值,fcntl(fd,F_SETLK,&结构体);
struct flock lock;
lock.type =F_RDLCK;
lock.whence = SEEK_SET;
lock.start = 3;
lock.len = 0;
fcntl(fd,F_SETLK,&lock);
引入: open 使用创建属性 O_CREAT, 0666
函数原型: int creat(const char *pathname, mode_t mode);
头文件:#include
#include #include
参数:pathname 文件名/文件路径
mode 创建模式 0666
先从终端 输入一个文件名,如果文件存在则打印存在,如果不存在则创建文件。 ./a.out danny.txt
练习:从终端输入文件名的参数,先判断文件是否存在,如果不存在则创建。
对文件枷锁后,只允许一个进程读写,其他进程等待读写,直到解锁。
struct flock {
...
short l_type; /* 锁的类型: F_RDLCK, F_WRLCK, F_UNLCK */
short l_whence; /*偏移量的起始位置,枷锁的参考位置 SEEK_SET, SEEK_CUR, SEEK_END */
off_t l_start; /* 正数表示往后偏移后枷锁, */
off_t l_len; /* 枷锁多少个字节 */ 如果字节数为0默认为偏移位置到文件结束。
pid_t l_pid; /* 锁的进程id号
...
};
代码:先定义机构体,给结构体成员赋值,fcntl(fd,F_SETLK,&结构体);
struct flock lock;
lock.type =F_RDLCK;
lock.whence = SEEK_SET;
lock.start = 3;
lock.len = 0;
fcntl(fd,F_SETLK,&lock);
参 数: timt_t为 long int类型 的指针 或者NULL
返 回 值:从公元1970年1月1号UTC 0点0分0秒到现在的秒数。
参 数: time_t 是从1970年1月1号0点到现在的时间 time();
返回值: 时间字符串信息,本地时间的日期和事件
作用:获取本地时间,会自动在时间字符串后面加 \r \n自动换行
作用:转换成真实世界所使用的时间和日期表示方式,返回给tm结构体
参数: time_t是从1970年到现在的秒数 time函数的返回值
返回值: tm结构体,保存的是真实世界的时间。
结构体信息如下:
struct tm {【重点】
int tm_sec; /* 秒数 [0-60] (1 leap second) */
int tm_min; /* 分钟数 [0-59] */
int tm_hour; /* 小时 [0-23] */
int tm_mday; /* 一个月中的日期 [1-31] */
int tm_mon; /* 月份[0-11] */
int tm_year; /* 年份 - 1900. */
int tm_wday; /* 周中的天数(星期数). [0-6] */
int tm_yday; /* 一年中的哪一天.[0-365] */
}
参数: tm* 结构体指针,保存的是国际时间的结构体信息
作用: 把国际时间转换成本地时间字符串
返回值:时间字符串
参 数: time_t 是time()函数的返回值
作 用: 把1970年1月1号到现在的秒数转换成本地时间
返 回 值: tm结构体指针
参数: tm*的结构体
返回值: time_t 在time函数中的参数中用过
作用: 把tm结构体的时间转换成1970年1月1号到现在的秒数。
头 文 件: #include #include #include
作 用: 获取文件的属性信息
参 数: pathname 文件路径或者文件名 buf 是结构体指针,保存的是文件属性信息
返 回 值: 如果成功返回0 如果是失败返回-1
结构体解析:
struct stat {
dev_t st_dev; /* 文件使用的设备号 */
ino_t st_ino; /* inode 设备的次设备号*/
mode_t st_mode; /* 文件的属性(目录 设备 链接)文件对应的模式 */
nlink_t st_nlink; /* 硬链接数*/
uid_t st_uid; /* 拥有者的用户ID */
gid_t st_gid; /* 组用户id */
dev_t st_rdev; /* 设备id */
off_t st_size; /* 文件的总字节数*/
blksize_t st_blksize; /* 磁盘块大小4k */
blkcnt_t st_blocks; /*占用的磁盘数 */
struct timespec st_atim; /* 最后一次访问的时间 */
struct timespec st_mtim; /* 最后一次修改的时间 */
struct timespec st_ctim; /* 最后一次改变状态的时间 */
}
详解stat结构体中的 st_mode;
总共16位 (short类型)
文件类型属性:(st_mode; 的前4位)
S_ISDIR(m) 目录文件
S_ISCHR(m) 字符设备
S_ISBLK(m) 块设备
S_ISFIFO(m) 管道设备
S_ISLNK(m) s连接设备
S_ISSOCK(m) 网络设备
文件权限( (st_mode; 的后9位))
S_IRWXU 00700 拥有者有可读可写可执行权限 rwx
S_IRUSR 00400 拥有者有可读权限 r
S_IWUSR 00200 拥有者有可写权限 w
S_IXUSR 00100 拥有者有可执行权限 x
S_IRWXG 组的权限 S_IRGRP S_IWGRP S_IXGRP
S_IRWXO (other)其他人权限 S_IROTH S_IWOTH S_IXOTH
#include
函数原型:int unlink(const char *pathname);
返回值: 0表示成功 -1表示失败
作用: 删除文件
函数原型: int truncate(const char *path, off_t length);
头文件:#include #include
参 数 : path 文件路径 或者文件名
length 需要截取的长度
返回值: 0表示成功 -1表示失败
函数作用: 从path文件中文件首位置开始截取length个字节。
#include
函数原型: int chmod(const char *pathname, mode_t mode);
参 数 : pathname 文件名
mode是文件新的权限 0666
作用: 修改文件权限
返回值: 如果成功返回0 失败返回-1
#include
#include
函数原型: mode_t umask(mode_t mask);
参数: mask 新的权限掩码
返回值: 返回老的权限掩码
#include
#include
函数原型:int mkdir(const char *pathname, mode_t mode); 【重点】
作用: 创建目录
参数: pathname 文件路径 目录路径 目录名
mode 创建权限 0777
返回值: 成功0 失败-1
#include
函数原型:int rmdir(const char *pathname); 【重点】
作用: 删除目录 (空目录)
参数: pathname 目录
返回值: 成功0 失败-1
#include
#include
函数原型: DIR *opendir(const char *name); 【重点】
作用: 打开目录 相当于cd 目录名
参数: name 目录名
返回值: dir的结构体指针
#include
#include
函数原型: int closedir(DIR *dirp); 【重点】
参数: dirp 是opendir的返回值
作用: 关闭目录
返回值: 成功0 失败-1
#include
函数原型: struct dirent *readdir(DIR *dirp); 【重点】
参数: dirp 结构体指针
返回值: 独到的文件或者目录信息 dirent结构体指针,失败返回NULL
作用: 读取目录,按照文件一个一个的读取。
返回值结构体:struct dirent {
ino_t d_ino; /* inode 数目 次设备号 inode节点 */
off_t d_off; /*现在偏移的位置*/
unsigned short d_reclen; /* 目录的长度 */
unsigned char d_type; /* 文件类型 d s l c b */
char d_name[256]; /* 文件名 */
};
struct dirent中d_type中的宏定义如下:
DT_BLK 块设备 DT_CHR 字符设备 DT_DIR 目录 DT_REG 普通文件 DT_LNK 连接
#include
#include
函数原型:void rewinddir(DIR *dirp);
函数参数: dirp 这个是open的返回值
作 用:重新设置目录的起始位置,把目录偏移设置到起始位置。
函数原型 void seekdir(DIR *dirp, long loc);
函数参数:dirp 是open的返回值
loc 是目录起始位置到偏移位置的长度(偏移量)
作用:设置目录的偏移
函数原型: long telldir(DIR *dirp);
函数参数: open的返回值 DIR*的结构体指针。
返 回 值: 当前目录的偏移位置
作 用: 查看当前目录的偏移位置
#include
函数原型: int chdir(const char *path);
函数作用: 跳转到某一个目录中
参数 : 需要跳转的目标目录
对源文件屏蔽 。
尊重开发者的知识产权
使用方便,方便开发。
本质是上库文件是可执行的二进制文件,可以被操作系统载入内存执行
库是别人写好,成熟的,可以重复使用的某一部分功能。
windows和linux的库文件是不一样的,不兼容。
1>在程序运行的时候加载到目标程序中,编译不检测。
2> 代码小 ,程序升级简单
3>实现资源共享
1>在程序编译阶段连接到目标文件
2>体积大,不利于程序的更新
3>程序运行运行过程中不需要静态库,移植方便。
注意:动态库和静态库是寄生虫,没办法单独运行,单独使用。
第一步:创建一个源文件和头文件 比如说xx.c xx.h
第二步: 在文件中按照多文件编译写需要的函数代码。
第三步:将库的源文件编译成目标文件 xx.o
gcc -c x.c -o xx.o
第四步: 将目标文件制作成静态库(lib库名.a)
命令:ar crs lib库名.a -o xxx.o
第五步: 编写测试代码 有main函数的代码(调用静态库函数)
第六步: 编译xx.c文件同时链接库文件
gcc -o abc xxx.c -L 目录 -l 库名
第七步:执行可执行程序
注意: gcc -o abc xxx.c -L 目录 -l 库名中 -L后不可以有空格, -l后也不可以有空格, -l后直接加静态库名称不需要加lib的前缀和.a后缀
第一步:创建一个源文件和头文件 比如说xx.c xx.h
第二步: 在文件中按照多文件编译写需要的函数代码。
第三步:将库的源文件编译成目标文件 xx.o
命令: gcc -fPIC -c xxx.c
fPIC 创建和地址无关的编译程序
第四步:将.o文件编译成动态库(产生了xx.so)
命令:gcc -shared -fPIC -o xxxx.so xxx.o
第五步: 编写测试代码 有main函数的代码(调用动态库函数)
第六步:将编译的xx.c文件编译连接动态库
命令: gcc -o abc xxx.c -L目录 -l库名
第七步: 运行可执行程序。
注意: 在编译完so文件后,需要放入到/lib 或者/usr/lib下
在gcc时 -L不可以省略 并且 -l后如果加空格则需要加动态库的后缀名 如果不加空格 则直接使用名称不用后缀。
运行时动态库必须存在,否则没法找到函数地址,运行出错,但静态库不会,编译完成后删除不会影响代码运行。