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

函数名: 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能否正常地进行读写操作呢?

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

 #include <stdio.h>

 int main(void)
{
char a;
int i = ;
FILE *fp = fopen("input.txt","r+");
a = getc(fp);
putc('b',fp);
putc('c',fp);
putc('d',fp);
putc('e',fp);
fclose(fp);
return ;
}

以上这段代码未能正常在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), , fp) == ){
/*对 rec 执行某些操作*/
if(/*rec 必须被重新写入*/){
fseek(fp, -(long)sizeof(rec), );
fwrite((char*)&rec, sizeof(rec), , fp);
}
}

这段代码乍看上去毫无问题:&rec 在传入 fread 和 fwrite 函数时被小心翼翼地转换为字 符指针类型,sizeof(rec)被转换为长整型(fseek 函数 求第二个参数是 long 类型,因为 int 类 型的整数可能无法包含一个文件的大小:sizeof 返回一个 unsigned 值,因此首先必须将其转 换为有符号类型才有可能将其反号)。但是这段代码仍然可能运行失败,而且出错的方式非 常难于察觉。

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

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

第二个 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), , fp) == ){
/*对 rec 执行某些操作*/
printf("record.a=%d\n", rec.a);
printf("record.b=%c\n", rec.b);
if(){
fseek(fp, -(long)sizeof(rec), );
rec.a=rec.b;
fwrite((char*)&rec, sizeof(rec), , fp);
fseek(fp, 0L, ); /*此句关系是否能写入*/
}
}
fclose(fp);
}

走进C标准库(4)——"stdio.h"中的putc的更多相关文章

  1. 走进C标准库(3)——"stdio.h"中的getc和ungetc

    接前文. 再来看看getc和ungetc的实现.在看这两个函数的实现之前,我们先来想一想这两个函数分别需要做的工作. int getc(FILE *stream) 说明:函数getc从stream指向 ...

  2. 走进C标准库(2)——"stdio.h"中的fopen函数

    其他的库文件看起来没有什么实现层面的知识可以探究的,所以,直接来看stdio.h. 1.茶余饭后的杂谈,有趣的历史 在过去的几十年中,独立于设备的输入输出模型得到了飞速的发展,标准C从这个改善的模型中 ...

  3. 走进C标准库(5)——"stdio.h"中的其他部分函数

    函数介绍来自:http://ganquan.info/standard-c/ 函数名: freopen 功  能: 替换一个流 用  法: FILE *freopen(char *filename, ...

  4. 走进C标准库(8)——"string.h"中函数的实现相关字符串操作函数

    我的strcat: char *strcat(char *dest,char *src) { char * reval = dest; while(*dest) dest++; while(*src) ...

  5. 走进C标准库(6)——"string.h"中函数的实现memchr

    我写的memchr: void *memchr(const void *buf, char ch, unsigned count){ unsigned ; while(*(buf++) != ch & ...

  6. 走进C标准库(7)——"string.h"中函数的实现memcmp,memcpy,memmove,memset

    我的memcmp: int memcmp(void *buf1, void *buf2, unsigned int count){ int reval; while(count && ...

  7. 走进C标准库(1)——assert.h,ctype.h

    默默觉得原来的阅读笔记的名字太土了,改了个名字,叫做走进C标准库. 自己就是菜鸟一只,第一次具体看C标准库,文章参杂了对<the standard C library>的阅读和对源码的一些 ...

  8. C语言中头文件<stdio.h>中的#ifndef _STDIO_H_

    先了解这里的相关知识:http://www.cnblogs.com/stemon/p/4000468.html 头文件的中的#ifndef,这是一个很关键的东西.比如你有两个C文件,这两个C文件都in ...

  9. C标准头文件<stdio.h>

    是很多人学C语言接触的第一个头文件,顾名思义,stdio就是"标准输入输出",其中声明了一组关于输入输出的类型,宏和函数,其中就包括了打印著名的"hello,world! ...

随机推荐

  1. 随机抽样一致性算法(RANSAC)

    本文翻译自维基百科,英文原文地址是:http://en.wikipedia.org/wiki/ransac,如果您英语不错,建议您直接查看原文. RANSAC是"RANdom SAmple ...

  2. JQuery获取Checkbox组的值

    前台: <div id="addtrtr" style="padding:20px; background-color:#F8F8F8;"> < ...

  3. Spring-----配置复杂对象

    转载自:http://blog.csdn.net/hekewangzi/article/details/51713037

  4. 开发移动端web应用, 使用手机自带键盘的搜索按钮

    很多时候在移动端的web页面中, 需要使用搜索功能, 然而页面中并没有太多的空间来放置一个像pc端上那样的搜索按钮, 这时候就需要借用手机输入法自带的搜索按钮来实现点击搜索 虽然不是什么大的功能, 但 ...

  5. solaris11.2下编译QT-配置命令

    1.make 版本为:GNU Make 3.82 2.gcc版本是:3.4.3(pkg install ...) 2.直接./configue是不行的 解决:./configue -platform ...

  6. SQL SERVER 2008 架构

    架构: 一个容器 包含表,视图,数据库对象等等. 相当于命名空间 如何创建一个架构: 1. 图形向导 2.命令 create schema 在sqlserver 2005中,可能大家在工作或学习的时候 ...

  7. JavaScript单线程

    首先浏览器的js引擎是单线程的,执行一个耗时操作必定阻碍线程后续代码的执行(比如等待网络请求的响应).一些语言采用了开一个子线程并把耗时操作放到子线程去执行的办法解决了这个问题.js引擎本身不支持多线 ...

  8. javascript第九课"闭包"

    所谓闭包:就是一个函数内部又定义了一个函数,而这个函数能访问外部函数作用域范围内的变量,这个内部函数就叫做闭包!   js中的面向对象都是使用闭包来实现的 闭包里使用的变量会现在当前函数内搜索,没有的 ...

  9. ODBC与JDBC比較

    在学习J2EE的JDBC过程中,刚见到JDBC就立即联想到了ODBC,并且我们能够肯定他们之间有必定的关系.開始学它的时候还是认为有点晕,于是就查了非常多资料,与比較熟悉的ODBC进行了比較. 先各自 ...

  10. jQuery模拟原生态App上拉刷新下拉加载

    jQuery模拟原生态App上拉刷新下拉加载效果代码,鼠标上拉时会显示loading字样,并且会模拟加载一条静态数据,支持触屏设备使用. <!doctype html> <html ...