fopen使用堆分配了一定数量的内存,当你用fprintf、putc等函数时,它
们先将要写的内容填入该内存区,填满了就FLUSH,真正地写入文件(当然,
这不是刷新磁盘缓冲),即调用INT 21,相当于执行write等函数。
而open呢,是直接调用INT 21,相应的读写函数write、read等也都是直
接调用INT 21。

在实际应用中,有这样两种情况:
  一、数据是大批大批地读写的,例如每次0.5K、1K、2K等。调用open会
直接一点,可以省去类似fprintf等函数的一些开销。

  二、数据是很零散的。例如整型数、长整型数、字符串、浮点数等说不
定要写些什么数据进去。如果一定要使用open + write。那么就得先
sprintf( buf, format, ... ),然后将buf write到文件中,这样频繁
地调用INT 21,将会浪费很多时间。这时使用fprintf( FILE, format,...)
反而更好了。

以上说的是在DOS下的情况,至于WINDOWS下及UNIX下是不是也有类似的区
别,我不太清楚(再说象WINDOWS这样,常常是希望用户“不拘小节”的)。



(一)
1.缓冲文件系统
缓冲文件系统的特点是:在内存开辟一个“缓冲区”,为程序中的每一个文件使用,当执行读文件的操作时,从磁盘文件将数据先读入内存“缓冲区”, 装满后再从内存“缓冲区”依此读入接收的变量。执行写文件的操作时,先将数据写入内存“缓冲区”,待内存“缓冲区”装满后再写入文件。由此可以看出,内存 “缓冲区”的大小,影响着实际操作外存的次数,内存“缓冲区”越大,则操作外存的次数就少,执行速度就快、效率高。一般来说,文件“缓冲区”的大小随机器 而定。
fopen, fclose, fread, fwrite, fgetc, fgets, fputc, fputs, freopen, fseek, ftell, rewind等
2.非缓冲文件系统
缓冲文件系统是借助文件结构体指针来对文件进行管理,通过文件指针来对文件进行访问,既可以读写字符、字符串、格式化数据,也可以读写二进制数 据。非缓冲文件系统依赖于操作系统,通过操作系统的功能对文件进行读写,是系统级的输入输出,它不设文件结构体指针,只能读写二进制文件,但效率高、速度 快,由于ANSI标准不再包括非缓冲文件系统,因此建议大家最好不要选择它。本书只作简单介绍。
open, close, read, write, getc, getchar, putc, putchar 等
(二)
open和fopen的区别:
前者属于低级IO,后者是高级IO。
前者返回一个文件描述符(用户程序区的),后者返回一个文件指针。
前者无缓冲,后者有缓冲。
前者与 read, write 等配合使用, 后者与 fread, fwrite等配合使用。
后者是在前者的基础上扩充而来的,在大多数情况下,用后者。




1、
open 是系统调用 返回的是文件句柄,文件的句柄是文件在文件描述副表里的索引,fopen是C的库函数,返回的是一个指向文件结构的指针。
2、
fopen的实现要调用open, 关键看你想返回什么了, FILE指针还是描述符?
3、
32位环境下,编译加“-D_FILE_OFFSET_BITS=64”
要在open里加O_LARGEFILE标记
[code]
static int ext2_open_file (struct inode * inode, struct file * filp)
{
        if (!(filp->f_flags & O_LARGEFILE) &&
            inode->i_size > 0x7FFFFFFFLL)
                return -EFBIG;
        return 0;
}
[/code]
0x7FFFFFFF就是2G-1。 看一下这个逻辑, 就知道open时必须制定O_LARGEFILE.
4、
fopen一个文件,fclose两次,会是什么样的情况?
本人测试了一下:
在aix,hp,sco,unixware上fclose第一次成功,fclose第二次返回错误
在linux上.fclose第一次成功,第二次报segmentation fault,程序吐核
5、
计算机语言遵循的规范称为标准。C语言有C标准,C++语言有C++标准。标准中详细规定了你能够做什么和不能做什么。标准中规定不能做的就是我说的标准的禁止事项。

其实不只是标准,在介绍学习或使用语言的书中也会出现大量的应该做和不应该做的说明,只是一般不可能象标准那样全面。所以,在“The C Programming Language”中没有提到这个未定义行为也没有什么奇怪的。

"程序员基本职责"?…… 可以认为是为了实现程序预定的目的,对程序员的基本要求。在使用语言上的一个基本要求就是要符合标准规定。

>> 还有一个问题
>> 在非linux系统下,我使用fopen打开的文件
>> 使用close关闭(注意,不是fclose),缓冲也相应关闭;
>> 而在linux系统下,如果使用close关闭,结果缓冲还残留在内存中.
>> 这个是不是说明linux 不支持标准?

如果用 close 来关闭一个缓冲文件,其行为是未定义的。因为 close 并不能保证刷新缓存,所以 fclose != close,不过可以这样认为:fclose = fflush + close。

既然 close 用 fopen 打开的文件是一种未定义行为,就是说标准没有规定这样会导致什么行为,所以结果导致缓冲刷新也罢不刷新也罢,这样的行为都是合法的,编译器可以自由实现。

由于未定义行为是标准规定的禁止在程序中出现的行为,所以一般没有必要再去讨论未定义行为到底有什么、或者有哪些具体的表现。

>> 那我觉得linux下编译的过程中就应该发出警告,fclose两次是未定义的,有潜在危险

想 法是不错的。只不过未定义行为不是语法错误,在编译阶段一般是发现不了的,或者实现起来相当困难,只好推移到运行阶段、借助于程序的行为异常来发现这些错 误。比如,fclose两次、除数为0、悬挂指针等带来的行为。当然,也可能因在运行时程序表现正常而发现不了这些错误。这就要求我们在写程序的时候就要 尽量处理好,不要出现未定义的行为。对于编译器而言呢,我们希望它会以一种尽量“醒目”的方式提示我们,因此编译器在对未定义行为的实现上其实是有自己的 考虑的。

>> 具体fclose两次是未定义的,这个是posix标准还是ansc  c标准呢?

从 man fclose 中可以看出,这是一个ansc  c标准规定的函数。这就意味着它又是一个 posix 标准的函数。posix就是在 ANSI C (C89)基础上实现的,包括了 ANSI C 的全部规定。

>> 但是我觉得一个标准是符合人们的习惯的,那就是如果我去调用一个库函数,
那么这个库函数只有一个出口就是返回值,而现在又出现另外的出口:SEGV信号

1. void函数没有返回值。2. 除了函数的返回值之外函数的参数也可以作为出口。

运行时出现了SEGV说明程序中存在错误。其根本原因在于程序员在程序中使用了未定义行为从而违反了标准所致。
6、
值得一提大家经常可能会遇到的就是strcpy这个函数,如果参数中有NULL值,
例如:strcpy(NULL,"aa")
很快就引起一个段错误.

现在软件要求的不仅仅是效率,那么对于这种情况我们该如何避免这种错误呢?
更多情况下我们使用变量copy,如strcpy(char *str1,char *str2);
为了避免错误,我们可能会在每次copy前判断
if(str1!=NULL)
    strcpy(str1,str2);
我并不觉得这样很麻烦,但是我们万一有个疏忽,某次忘记了
不就造成大错了吗?

解决办法是我们要在原来c库的基础上进行我们的包装
写一个自己的类库,例如改装成
my_safe_strcpy(char *,char *);
但是这样的做法好象不太多见,大家都是直接使用c的库就OK;
7、
int setvbuf(FILE *stream, char *buf, int mode , size_t size);
把缓冲区与流相联,通过改变buf的大小,的确能改变fopen打开的流按系统默认buffer输出。
8、
前者属于低级IO,后者是高级IO。
前者返回一个文件描述符,后者返回一个文件指针。
前者无缓冲,后者有缓冲。
前者与 read, write 等配合使用, 后者与 fread, fwrite等配合使用。
后者是在前者的基础上扩充而来的,在大多数情况下,用后者。
9、
好象是fopen返回给fp的值有问题 因为这个所以导致fclose的时候coredump


台南小新 發表在 痞客邦 PIXNET 留言(0) 人氣()