走进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能否正常地进行读写操作呢?
使用一个简单的样例测试了一下:
#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的更多相关文章
- 走进C标准库(3)——"stdio.h"中的getc和ungetc
接前文. 再来看看getc和ungetc的实现.在看这两个函数的实现之前,我们先来想一想这两个函数分别需要做的工作. int getc(FILE *stream) 说明:函数getc从stream指向 ...
- 走进C标准库(2)——"stdio.h"中的fopen函数
其他的库文件看起来没有什么实现层面的知识可以探究的,所以,直接来看stdio.h. 1.茶余饭后的杂谈,有趣的历史 在过去的几十年中,独立于设备的输入输出模型得到了飞速的发展,标准C从这个改善的模型中 ...
- 走进C标准库(5)——"stdio.h"中的其他部分函数
函数介绍来自:http://ganquan.info/standard-c/ 函数名: freopen 功 能: 替换一个流 用 法: FILE *freopen(char *filename, ...
- 走进C标准库(8)——"string.h"中函数的实现相关字符串操作函数
我的strcat: char *strcat(char *dest,char *src) { char * reval = dest; while(*dest) dest++; while(*src) ...
- 走进C标准库(6)——"string.h"中函数的实现memchr
我写的memchr: void *memchr(const void *buf, char ch, unsigned count){ unsigned ; while(*(buf++) != ch & ...
- 走进C标准库(7)——"string.h"中函数的实现memcmp,memcpy,memmove,memset
我的memcmp: int memcmp(void *buf1, void *buf2, unsigned int count){ int reval; while(count && ...
- 走进C标准库(1)——assert.h,ctype.h
默默觉得原来的阅读笔记的名字太土了,改了个名字,叫做走进C标准库. 自己就是菜鸟一只,第一次具体看C标准库,文章参杂了对<the standard C library>的阅读和对源码的一些 ...
- C语言中头文件<stdio.h>中的#ifndef _STDIO_H_
先了解这里的相关知识:http://www.cnblogs.com/stemon/p/4000468.html 头文件的中的#ifndef,这是一个很关键的东西.比如你有两个C文件,这两个C文件都in ...
- C标准头文件<stdio.h>
是很多人学C语言接触的第一个头文件,顾名思义,stdio就是"标准输入输出",其中声明了一组关于输入输出的类型,宏和函数,其中就包括了打印著名的"hello,world! ...
随机推荐
- IIS Could not load file or assembly 'CLDBCommon.DLL' or one of its dependencies.找不到指定的模块
1.卸载原来的.NET4.0,从新下载.NET4.5.1完整安装程序.后问题解决附:.NET4.5.1下载地址:https://www.microsoft.com/zh-cn/download/det ...
- HDU 4415 - Assassin’s Creed
Problem Description Ezio Auditore is a great master as an assassin. Now he has prowled in the enemie ...
- HTTP中的URL长度限制(资料整理)
HTTP中的URL长度限制 首先,其实http 1.1 协议中对url的长度是不受限制的,协议原文: The HTTP protocol does not place any a priori l ...
- css中element element和element>element选择器的区别
就是这样的选择器: 比如html中有这样一段布局: <div> <p>我是一个段落</p> </div> 这时你用div p{background:ye ...
- lua学习笔记(1)-基本语法
==============变量类型nilnumber(实数) 1 2 3.14 7.65e8string "hello world" "\n ...
- ASP.NET页面继承关系
用过ASP.NET(以下简称ASP)的都知道ASP以一种Code Behind的方式给咱展现了一种类似Winform的开发模型,同样也是以"事件触发"的方式进行各种请求处理.其中A ...
- Oracle EBS-SQL (WIP-16):检查期间手工下达的车间任务数.sql
select WE.DESCRIPTION 任务说明, --DECODE( ...
- css案例学习之div与span的区别
代码: <html> <head> <title>div 标记范例</title> <style type="text/css" ...
- react-native学习笔记——简单尝试
毫无疑问,我是个不善于写博文的人. 毫无疑问,react是个出的框架. 毫无疑问,react-native更是个牛逼的引擎. 我个人对react-native的理解就是js被js引擎编译,去调用本地语 ...
- 【找规律】CodeForce #258 Problem A——Game With Sticks
来源:点击打开链接 非常easy.找规律. 每去掉一个点,点的总数量就减去n+m-1,然后看谁最先减到没有点可减.就能够了. #include <iostream> #include &l ...