标签:for ges 解释 函数 cpu 文本 路径 常见 ima
一、标准IO的效率
对比以下四个程序的用户CPU、系统CPU与时钟时间对比
程序1:系统IO
程序2:标准IO getc版本
程序3:标准IO fgets版本
结果:
【注:该表截取自APUE,上表中"表3-1中的最佳时间即《程序1》","表3-1中的单字节时间指的是《程序1》中BUFSIZE为1时运行时间结果",fgetc/fputc版本程序这里没放出】
对于三个标准IO版本的每一个其用户CPU时间都大于最佳read版本,因为每次读一个字符版本中有一个要执行150万次的循环,而在每次读一行的版本中有一个要执行30000次的循环。而在read版本中,其循环只需执行180次。因为系统CPU时间都相同,所以用户CPU时间的差别造成了时钟时间的差别。系统CPU时间相同的原因是所有这些程序对内核提出的读写请求数相同。
上表中最后一列是每个main函数的文本空间字节数(由c编译产生的机器指令)。从中可见getc/putc版本在文本空间做了大量宏替换,所以它所需的指令数超过了调用fgetc/fputc函数所用的指令数。从用户CPU时间看getc/putc版本与fgetc/fputc版本在此次测试中并没有多大的差别。
使用每次一行IO的版本其速度大约是每次一个字符版本的两倍(包括用户CPU时间和时钟时间)。如果fgets/fputs函数用getc/putc实现则可以预计fgets版本的时间会与getc版本相接近。可以预料每次一行的版本会更慢一些,因为除了现存的60000次函数调用外还需增加3百万次宏调用。而在本测试中每次一行参数是用memccoy实现的,为了提高效率memccpy函数用汇编写。
【重点】fgetc版本与程序1 BUFSIZE=1的版本要快得多,两者都用了约3百万次函数调用,造成速度差距这么大的原因在于《程序1》执行了3百万次函数调用这也执行了3百万次系统调用,而fgetc版本虽然执行了3百万次函数调用但是只引起了360次系统调用。系统调用与普通的函数调用相比是很耗时间的。
二、二进制IO
为了可以读取二进制文件我们可以通过getc/putc实现的,但是这样必须循环整个结构。而fputs/fgets在遇到null字符时就结束,在结构中可能含有null字节,所以不能使用fgets/fputs。综上所以提供了下面两个函数以执行二进制IO操作
#include <stdio.h> size_t fread(void *ptr, size_t size, size_t nobj, FILE *fp); size_t fwrite(const char *ptr, size_t size, size_t nobj, FILE *fp); 返回值:读或写的对象数
常见的用法:
float data[10]; if (fwrite(&data[2], sizeof(float), 4, fp) != 4) { fprintf(stderr, "fwrite error"); }
其中,指定size为每个数组元素的长度,nobj为欲写的元素数。
读或写一个结构。例:
struct { short count; long total; char name[NAMESIZE]; } item; if (fwrite(&item, sizeof(item), 1, fp) != 1) { fprintf(stderr, "fwrite error"); }
对于读,如果出错或到达文件尾,则fread返回的数字可能少于nobj。这时应该调用ferro+feof判断是哪种情况。对于写如果返回之小于nobj则出错。
使用二进制IO的限制是只能用于读已写在同一系统上的数据。但是现在很多异构系统通过网络连接在一起,通常会在一个系统上读取另外一个系统上的数据,这样的话这两个函数就不能工作了,原因:
三、 定位流
有两种方式可以定位标准IO流。
需要移植到非UNIX系统上运行的程序应使用fgetpos和fsetpos。
#include <stdio.h> long ftell(FILE *fp); 返回值:成功则为当前位置相对于文件首的偏移字节数,出错为-1L int fseek(FILE *fp, long offset, int whence); 返回值:成功为0,出错为非0 void rewind(FILE *fp);
对于一个二进制文件,其位置指示是从文件起始位置开始度量并以字节为单位的。ftell用于二进制文件时,其返回值就是这种字节位置。为了用fseek定位一个二进制文件,必须指定一个字节offset,以及解释这种位移量的方式。whence与lseek函数相同:SEEK_SET表示从文件的起始位置开始,SEEK_CUR表示从当前位置,SEEK_END表示从文件的尾端。
对于文本文件,它们的文件当前位置可能不以简单的字节位移量来度量。在非UNIX系统中可能以不同的格式存放文本文件,为了定位一个文本文件,whence一定要是SEEK_SET,而且offset只能有两种值:0(表示反绕文件到其起始位置),或者是对该文件的ftell所返回的值。使用rewind函数也可以将一个流设置到文件的起始位置。
#include <stdio.h> int fgetpos(FILE *fp, fpos_t *pos); int fsetpos(FILE *fp, const fpos_t *pos); 返回值:成功为0,出错非0
fgetpos将当前位置存入pos指向的对象中。在以后调用fsetpos时,可以使用此值将流重定向至该位置。
四、 格式化IO
1. 格式化输出
#incldue <stdio.h> in printf(const char *format, ...);
返回值:成功则为输出字符数,出错为负值
int fprintf(FILE *fp, const char *format, ...);
返回值:成功则为输出字符数,出错为负值
int sprintf(char *buf, const char *format, ...);
返回值:存入数组的字符数
sprintf将格式化的字符送入数组buf中。sprintf在该数组的尾端自动加一个null字节,但该字节不包含在返回值中。sprintf有可能会使buf指向的缓存溢出。
printf族的三种变体类似于上面的三种,只不过是可变参数变成了arg
#include<stdarg.h> #include<stdio.h> int vprintf(const char * f o r m a t, va_list arg) ; int vfprintf(FILE *f p, const char * f o r m a t, va_list arg) ; 两个函数返回:若成功则为输出字符数,若输出出错则为负值 int vsprintf(char *b u f, const char * f o r m a t, va_list arg) ; 返回:存入数组的字符数
2. 格式化输入
三个scanf函数:
#include <stdio.h> int scanf(const char *format, ...); int fscanf(FILE *fp, const char *format, ...); int sscanf(const char *buf, const char *format, ...);
五、实现细节
在UNIX中标准IO最终都要调用系统IO。每个IO流都有一个与其关联的文件描述符,可以用fileno获取该流对应的文件描述符。
#include <stdio.h> int fileno(FILE *fp); 返回值:与流相关联的文件描述符
为了了解所使用的系统中标准IO的实现最好从stdio.h头文件开始。
【注:原书中下面有一个案例这里没有放出】
六、临时文件
标准IO库提供了两个函数以帮助创建临时文件
#include <stdio.h> char *tmpnam(char *ptr); 返回值:指向一唯一路径名的指针 FILE *tmpfile(void); 返回值:成功则为文件指针,出错为NULL
tmpnam产生一个与现在文件名(改文件名不是指ptr!该函数用来产生一个唯一文件)不同的一个有效路径名字符串。每次调用它时,它都产生一个不同的路径名,最多调用次数是TMP_MAX。TMP_MAX定义在<stdio.h>中
若ptr是NULL,则所产生的路径名存放在一个静态区中,指向该静态区的指针作为函数值返回。下一次再调用tmpnam时会重写该静态区。(这意味着如果我们调用此函数多次,而且想保存路径名,那我们应该保存该路径名的副本而不是指针的副本) 如果ptr不是NULL,则认为它指向长度至少是L_tmpnam个字符的数组。(常数L_tmpnam定义在<stdio.h>中)所产生的路径名存放在该数组中,ptr也作为函数值返回。
tmpfile创建一个临时二进制文件。在关闭该文件或程序结束时会自动删除这种文件。
tempnam是tmpnam的一个变体,它允许调用者为所产生的路径名指定目录和前缀。
#include <stdio.h> char *tempnam(const char *directory, const char *prefix); 返回值:指向一唯一路径名的指针
对于目录有四种不同的选择:(优先级从高至低)
(1) 如果定义了环境变量TMPDIR,则用其作为目录。
(2) 如果参数directory非NULL,则用其作为目录。
(3) 将<stdio.h>中的字符串P_tmpdir用作为目录。
(4) 将本地目录,通常是/tmp用作为目录。
如果prefix非NULL,则它通常是最多包含5个字符的字符串,用其作为文件名的前几个字符。
该函数调用malloc函数分配动态存储区,用其存放所构造的路径名。当不再使用该路径名时就可释放次存储区。
标签:for ges 解释 函数 cpu 文本 路径 常见 ima
原文地址:http://www.cnblogs.com/orlion/p/6235006.html