有关库函数的使用,我们能给出的最好建议是尽量使用系统头文件,当然也可以自己造轮子,随个人喜好。本章将探讨某些常用的库函数,以及编程者在使用它们的过程中可能出错之处。
5.1 返回整数的getchar函数
我们首先考虑下面的例子:
#include<stdio.h>
int main()
{
char c;
while( ( (c = getchar()) != EOF ) )
putchar(c);
return 0;
}
上面代码在某些情况下可能出错,原因在于变量 c 被声明为 char 类型,而不是 int 类型。这意味着 c 无法容纳下所有可能的字符,(有可能发生“截断” )特别是,可能无法容纳下EOF。
因此可能出现两种可能。一种可能是,某些合法的输入字符在被“截断” 后使得 c 的取值与EOF相同;另一种可能是,c 根本不可能取到EOF这个值。对于前一种情况,程序将在文件复制的中途终止;对于后一种情况,程序将陷入一个死循环。
5.2 更新顺序文件
看下面这段代码:
FILE *fp;
fp = fopen(filename,"r+");
struct record rec;
.....
while(fread( fread( (char*)&rec, sizeof(rec), 1, fp ) == 1 )
{
if(/*rec必须被重新写入*/)
{
fseek(fp, -(long)sizeof(rec), 1); //文件指针向前移动一个结构体大小的长度
fwrite( (char*)&rec, sizeof(rec), 1, fp );
}
}
这段代码咋看上去毫无问题: &rec 在传入 fread 和 fwrite 函数时被小心翼翼地转换为字符指针类型,sizeof(rec)被转换为长整型 ( fseek 函数要求第二个参数是long类型,因为 int 类型的整数可能无法包含一个文件的大小;sizeof返回一个unsigned值,因此首先将其转换为有符号类型才有可能将其反号)。但是这段代码仍然可能运行失败,而且出错的方式非常难以察觉。
问题出在:为了保持与过去不能同时进行读写操作的程序的向下兼容性,一个输入操作不能随后直接紧跟一个输出操作,反之亦然。如果要同时进行输入和输出操作,必须在其中插入 fseek 函数的调用,即使fseek什么也没做。解决的办法是把这段代码改写为:
while(fread( fread( (char*)&rec, sizeof(rec), 1, fp ) == 1 )
{
if(/*rec必须被重新写入*/)
{
fseek(fp, -(long)sizeof(rec), 1); //文件指针向前移动一个结构体大小的长度
fwrite( (char*)&rec, sizeof(rec), 1, fp );
fseek(fp, 0L, 1);
}
}
第二个 fseek 函数虽然看上去什么也没做,但它改变了文件的状态,使得文件现在可以正常地进行读取了。
5.3 缓存输出与内存分配
当一个程序生成输出时,.....(未完待续)