C 库函数 fopen
使用给定的模式 mode 打开 filename 所指向的文件。
FILE *fopen(const char *filename, const char *mode)
mode也可以包含字母b,放在最后或者中间,表示二进制流。例如“rb”,“r+b”;(如果你的程序以后要移植到windows环境下可以加上b,在linux环境下则不需要,linux只有流的概念,没有二进制的概念)
errno
来标识错误。该全局变量在头文件errno.h
中声明:(只展示部分)#define EPERM 1 /* Operation not permitted */
#define ENOENT 2 /* No such file or directory */
#define ESRCH 3 /* No such process */
#define EINTR 4 /* Interrupted system call */
#define EIO 5 /* I/O error */
#define ENXIO 6 /* No such device or address */
#define E2BIG 7 /* Argument list too long */
代码示例
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
int main(){
FILE *fp;
fp=fopen("temp","r");
if(fp==NULL){
fprintf(stderr,"fopen() failed! errno = %d\n",errno);
exit(1);
}
puts("OK");
exit(0);
}
编译执行后打印结果:
可知errno为2,为No such file or directory;
在C标准中定义了两个函数帮助打印输出errno的对应错误原因,一个是strerror,另一个是perror;
perror包含在stdio.h
中:
//函数原型
/*
*功能:根据error打印对应的错误信息
*参数:s: 用户自定义信息字符串,一般是出错的函数名
*/
void perror(const char *s);
修改后的程序为:
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
int main(){
FILE *fp;
fp=fopen("temp","r");
if(fp==NULL){
//fprintf(stderr,"fopen() failed! errno = %d\n",errno);
perror("errno");
exit(1);
}
puts("OK");
exit(0);
}
打印结果:
strerror
包含在<string.h>
头文件中
//函数原型
/*
*功能:将某个错误代码转换成对应的错误信息
*/
char *strerror(int errnuum);
修改后的程序为:
fprintf(stderr, "fopen:%s\n", strerror(errno));
fopen函数解析:
由函数原型可知,fopen函数返回的是一个FILE类型的指针,FILE是一个结构体,由typedef进行了重命名,而指针实际上是指向结构体的指针。
问题:指针指向的内存空间是哪一块(或者说FILE结构体放在内存的哪一块)?是堆,是栈,还是静态区?
1、栈
// 简单的fopen源码分析
FILE *fopen(const char *filename, const char *mode) {
FILE tmp;
// 给结构体成员赋值初始化
tmp.xxx = xxx;
tmp.yyy = yyy;
...
return &tmp;
}
分析:tmp变量的存储类别是自动类型(块作用域,自动存储期),当程序退出这个块时,释放刚才为变量tmp匹配的内存,因此,指针指向的地址实际上没有tmp,是一个没有被分配的内存;
2、静态区
// 简单的fopen源码分析
FILE *fopen(const char *filename, const char *mode) {
static FILE tmp;
// 给结构体成员赋值初始化
tmp.xxx = xxx;
tmp.yyy = yyy;
...
return &tmp;
}
加上static
,将tmp保存在静态区(静态无链接),但是只能存在一个FILE实例(因为只有这一个内存区供指针指向);调用10次fopen函数,内存还是只有这一块,打开第一个文件时,是正确的,但在打开第二个文件时,就会把第一个的结果覆盖掉,第一个文件就没法操作了,会出错。例如:
fp1 = fopen("a", "r");
fp2 = fopen("b", "r");
// 此时fp1实际指向了b,第二次的结果会把第一次的结果覆盖掉
3、堆(正解)
// 简单的fopen源码分析
FILE *fopen(const char *filename, const char *mode) {
FILE *tmp = NULL;
tmp = malloc(sizeof(FILE));
// 给结构体成员赋值初始化
tmp->xxx = xxx;
tmp->yyy = yyy;
...
return tmp;
}
此时变量tmp
具有动态存储期,从调用malloc
分配内存到调用free
释放内存为止,而free
就在fclose
函数中被调用。