走进C标准库(4)——"stdio.h"中的putc

花了点时间把园子弄得好看了点,现在继续。

函数名: putc 
功  能: 输出一字符到指定流中 
用  法: int putc(int ch, FILE *stream); 

#define _putc_lk(_c,_stream)    (--(_stream)->_cnt >= 0 ? 0xff & (*(_stream)->_ptr++ = (char)(_c)) :  _flsbuf((_c),(_stream)))

看到这个宏是否觉得很熟悉,很像getc的宏吧。

那么,很容易产生一个问题,同样是改变IO控制块中的信息,同时使用putc和getc能否正常地进行读写操作呢?

使用一个简单的样例测试了一下:

 1 #include <stdio.h>
 2 
 3 int main(void)
 4 {
 5    char a;
 6    int i = 0;
 7    FILE *fp = fopen("input.txt","r+");
 8    a = getc(fp);
 9    putc('b',fp);
10    putc('c',fp);
11    putc('d',fp);
12    putc('e',fp);
13    fclose(fp);
14    return 0;
15 }

以上这段代码未能正常在input.txt文件中写入内容。

通过单步调试可以看到,getc是成功的,putc也是改变了缓冲区的,但是在写入文件这一步出了问题。

更进一步,比较了在fopen后,先进行getc和先进行putc,fp的各成员的值的异同。可以看到,当先进行getc时,fp->_flag=137,而当先进行putc时,fp->_flag=138。那么,显然在第一次使用函数读写fp时,就会为fp定下不同的读写标记。该标记会阻止其他类型的操作。

于是,在上述代码的第8行下加入代码:fp->_flag = 138后,putc成功。

从缓冲区的意义来说,不会说不允许同时支持读写。原因可能是同时读写文件在是实际意义上会造成读信息或写信息的混乱,亦或者可能是实现上的原因。猜测。

言归正传:

那么putc只有在FILE中为写信息的时候才生效。

当stream->_cnt大于0(缓冲区还有写空位),就继续写,如果没有空位了就调用_flsbuf((_c),(_stream)),通过该函数调用WriteFile,将缓冲区中的内容写入到文件中,然后重置_ptr的位置和_cnt的大小。

简单的逻辑。

后附《C陷阱与缺陷》中关于上述现象的描述。

5.2更新顺序文件

许多系统中的标准输入/输出库都允许程序打开一个文件,同时进行写入和读出的操作:

FILE *fp;
fp=fopen(file, "r+");

上面的例子代码打开了文件名由变量 file 指定的文件,对于存取权限的设定表明程序希望对这个文件进行输入和输出操作。

编程者也许认为,程序一旦执行上述操作完毕,就可以自由地交错进行读出和写入的操 作。遗憾的是,事实总难遂人所愿,为了保持与过去不能同时进行读写操作的程序的向下兼性,一个输入操作不能随后直接紧跟一个输出操作,反之亦然。如果  同时进行输入和输 出操作,必须在其中插入 fseek 函数的调用。

下面的程序片段似乎更新了一个顺序文件中选定的记录:

FILE *fp;
struct record rec;
...
while(fread((char*)&rec, sizeof(rec), 1, fp) == 1){
    /*对 rec 执行某些操作*/ 
    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 值,因此首先必须将其转 换为有符号类型才有可能将其反号)。但是这段代码仍然可能运行失败,而且出错的方式非 常难于察觉。

问题出在:如果一个记录需 被重新写入文件,也就是说,fwrite 函数得到执行,对这 个文件执行的下一个操作将是循环开始的 fread 函数。因为在 fwrite 函数调用与 fread 函数 调用之间缺少了一个 fseek 函数调用,所以无法进行上述操作。解决的办法是把这段代码改 写为:

while(fread((char*)&rec, sizeof(rec), 1, fp) == 1){
    /*对 rec 执行某些操作*/ 
    if(/*rec 必须被重新写入*/){
        fseek(fp, -(long)sizeof(rec), 1); 
        fwrite((char*)&rec, sizeof(rec), 1, fp); 
        fseek(fp, 0L, 1);
    }
}

第二个 fseek 函数虽然看上去什么也没做,但它改变了文件的状态,使得文件现在可以 正常地进行读取了。

附录测试文件:

#include <stdio.h>


int main()
{
    FILE *fp; 
    struct record{
        char a; 
        char b;
    };
 

    struct record rec;


    if((fp=fopen("test", "rb+"))==NULL)
    {
        printf("open error!\n");
    }
    while(fread((char*)&rec, sizeof(rec), 1, fp) == 1){
    /*对 rec 执行某些操作*/ 
        printf("record.a=%d\n", rec.a); 
        printf("record.b=%c\n", rec.b); 
        if(1){
            fseek(fp, -(long)sizeof(rec), 1); 
            rec.a=rec.b;
            fwrite((char*)&rec, sizeof(rec), 1, fp); 
            fseek(fp, 0L, 1); /*此句关系是否能写入*/
        }
    }
    fclose(fp);
}
就算黑夜也有星光。
原文地址:https://www.cnblogs.com/shijiezhenmei/p/3679558.html