Linux系統編程2標準IO - fopen

實驗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$ 

實驗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(),

小技巧:如果一個函數的返回值是指針,會有一個與該函數對應的逆操作,此時基本可以確認該函數返回的指針執行的空間是堆空間。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章