学习自李慧琴老师
实验1 fopen.c :在当前路径下打开一个不存在的文件 “tmp”
实验2:man手册中的头文件一定要全部加进去、
int *p = malloc(sizeof(int));
问题: FILE *fopen(const char *path, const char *mode); 返回的结构体指针所指向的空间是哪里??
一 概述
#include <stdio.h>
/*
参数 const char *path,文件名,可以相对路径或绝对路径
参数 const char *mode, 权限
返回值 FILE *
*/
FILE *fopen(const char *path, const char *mode);
1.1 权限mode说明
man fopen 可以查看关于 fopen() mode权限的说明:
The argument mode points to a string beginning with one of the following sequences (possibly followed by additional characters, as described below):
即指向一个以下列字符开头的字符串,只要开头是下列字符即可,后续字符不重要
rasdfghj == r
wsdfgh == w
r+fghjhj == r+
r 和 r+形式打开文件的时候,要求文件必须存在。
r : Open text file for reading. The stream is positioned at the beginning of the file.
只读方式打开文件,当前文件位置指针定位到文件起始位置
r+ :Open for reading and writing. The stream is positioned at the beginning of the file.
以读写方式打开文件,当前文件位置指针定位到文件起始位置
以下为无则创建方式:
w :Truncate(截断) file to zero length or create text file for writing. The stream is positioned at the beginning of the file.
有则清空文件,无则以只写方式创建文件,当前文件位置指针定位到文件起始位置
w+:Open for reading and writing. The file is created if it does not exist, otherwise it is truncated. The stream is positioned at the beginning of the file.
有则清空文件,无则以读写方式创建文件,当前文件位置指针定位到文件起始位置
a:Open for appending (writing at end of file). The file is created if it does not exist. The stream is positioned at the end of the file.
有则以追加只写的方式打开文件,写到文件的末尾处,无则创建文件,当前文件位置指针定位到文件末尾位置(文件的最后一个有效字节的下一个位置)
a+ :Open for reading and appending (writing at end of file). The file is created if it does not exist. The initial file position for reading is at the beginning of the file, but output is always appended to the end of the file.
有则以读写方式打开文件,读方式打开的时候,当前文件指针定位到文件起始位置,写方式打开的时候,当前文件位置指针定位到文件末尾位置,无则创建文件。
The mode string can also include the letter ‘b’ either as a last character or as a character between the characters in any of the two-character strings described above. This is strictly for compatibility with C89 and has no effect; the ‘b’ is ignored on all POSIX conforming systems, including Linux. (Other systems may treat text files and binary files differently, and adding
the ‘b’ may be a good idea if you do I/O to a binary file and expect that your program may be ported to non-UNIX environments.)
模式字符串还可以将字母“b”作为最后一个字符,或者作为上述任何两个字符字符串中的字符之间的字符。但是在所有与POSIX兼容的系统上(包括Linux),“b”都被忽略。(其他系统可能会以不同的方式对待文本文件和二进制文件,并进行添加,如果您对二进制文件执行I/O操作,并且希望将程序移植到非unix环境中,那么“b”可能是个好主意。
在Windows环境下其实是包含两种流的,一种是文本流,一种是二进制流,这这两种流在程序当中的控制是不一样的,所以在Windows环境下开发,要指定是以 r 方式打开 ,还是以rb方式打开,也就是把当前流作为哪种流的形式打开。
Linux 环境下只有一种流,即文本流 stream ,在Linux 环境下没有必要加b,但是如果程序后面可能移植到windows环境下跑,那么要加b
1.2 返回值
RETURN VALUE
Upon successful completion fopen(), fdopen() and freopen() return a FILE pointer. Otherwise, NULL is returned and errno is set to indicate the error.
成功返回 FILE pointer,失败返回对应errno(ionumber)
1.2.1 errno宏说明
errno说明:它是宏
/usr/include/asm-generic
errno-base.h
errno.h
errno-base.h 中就是我们常见的报错宏值与宏名
mhr@ubuntu:/usr/include/asm-generic$ cat errno-base.h
#ifndef _ASM_GENERIC_ERRNO_BASE_H
#define _ASM_GENERIC_ERRNO_BASE_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 */
#define ENOEXEC 8 /* Exec format error */
#define EBADF 9 /* Bad file number */
#define ECHILD 10 /* No child processes */
#define EAGAIN 11 /* Try again */
#define ENOMEM 12 /* Out of memory */
#define EACCES 13 /* Permission denied */
#define EFAULT 14 /* Bad address */
#define ENOTBLK 15 /* Block device required */
#define EBUSY 16 /* Device or resource busy */
#define EEXIST 17 /* File exists */
#define EXDEV 18 /* Cross-device link */
#define ENODEV 19 /* No such device */
#define ENOTDIR 20 /* Not a directory */
#define EISDIR 21 /* Is a directory */
#define EINVAL 22 /* Invalid argument */
#define ENFILE 23 /* File table overflow */
#define EMFILE 24 /* Too many open files */
#define ENOTTY 25 /* Not a typewriter */
#define ETXTBSY 26 /* Text file busy */
#define EFBIG 27 /* File too large */
#define ENOSPC 28 /* No space left on device */
#define ESPIPE 29 /* Illegal seek */
#define EROFS 30 /* Read-only file system */
#define EMLINK 31 /* Too many links */
#define EPIPE 32 /* Broken pipe */
#define EDOM 33 /* Math argument out of domain of func */
#define ERANGE 34 /* Math result not representable */
注意:
man手册中的头文件一定要全部加进去。
实验1 fopen.c :在当前路径下打开一个不存在的文件 “tmp”
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
int main()
{
FILE *fp;
//
fp = fopen("tmp","r");
if(fp == NULL)
{
fprintf(stderr,"fopen() failed! errno = %d\n",errno);
//更清楚的查看出错原因,两个很好用的函数perror,strerror
perror("fopen()");
fprintf(stderr,"fopen():%s\n",strerror(errno));
exit(1);
}
puts("OK!");
exit(0);
}
mhr@ubuntu:~/work/linux/stdio$ ./a.out
fopen() failed! errno = 2
fopen(): No such file or directory
fopen():No such file or directory
mhr@ubuntu:~/work/linux/stdio$
perror("fopen()");
NAME
perror - print a system error message
perror()函数在标准错误上生成一条消息,描述在调用系统函数或库函数时遇到的最后一个错误。
SYNOPSIS
#include <stdio.h>
void perror(const char *s);
#include <errno.h>
const char * const sys_errlist[];
int sys_nerr;
int errno; /* perror()会主动关联到 errno, 即err number ,所以也要加头文件errno.h */
strerror()
NAME
strerror, strerror_r, strerror_l - return string describing error number
返回描述错误号的字符串
SYNOPSIS
#include <string.h>
char *strerror(int errnum);
实验2:man手册中的头文件一定要全部加进去。
如下语句:
int *p = malloc(sizeof(int));
int型指针指向一个大小为一个整形值大小的空间,这段代码会被警告 两边类型不匹配。
为什么?
malloc() 的返回值是 void* ,malloc()所在的头文件是 stdib.h标准库头文件,如果没有包含该头文件,会造成如下现象,gcc看到 int p = malloc(sizeof(int)); 的时候,由于没包含头文件,gcc会认为所有函数的返回值都是整型,把一个整幸值赋值给一个整型指针,当然会警告。加上文件,返回void 类型指针,void* 类型指针赋值给任何类型指针都是可以的,反之异常。
问题; FILE *fopen(const char *path, const char *mode); 返回的结构体指针所指向的空间是哪里??
三种情况之一。栈,静态区,堆
情况1,栈
不会是栈,栈空间数据会在函数调用结束后被清空
情况2,静态存储区
如果是静态区,静态区数据确实可以完整的保留,直到进程结束为止。但是 static 修饰的静态区变量有一个最大的特点,即不论该函数被调用多少次,该static 静态区变量声明只被一次,如
所以 不论调用多少次fopen(),如果 FILE 结构体位于静态区,那么该函数内部的 static FILE tmp 将只被执行一次,后面的无论调用多少次 fopen,都不会再被执行,也就是说除第一次调用fopen 之外,后面所有的fopen ,不论打开什么文件,返回的都是第一次开的文件信息,这样是不行的。
情况3,堆 ,可行的
所以 fclose()中会有一个 free(),
小技巧:如果一个函数的返回值是指针,会有一个与该函数对应的逆操作,此时基本可以确认该函数返回的指针执行的空间是堆空间。