走进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! ...
随机推荐
- C#.net 摄像头驱动程序,用avicap32.dll
装了摄像头后一般会有 avicap32.dll文件,没有一样可以用这些代码. 不需要在解决方案里面引用这个.dll文件. 下面有二种写法的例子: 例一: using System;using Syst ...
- UVa 202 - Repeating Decimals
给你两个数,问你他们相除是多少,有无限循环就把循环体括号括起来 模拟除法运算 把每一次的被除数记下,当有被除数相同时第一个循环就在他们之间. 要注意50个数之后要省略号...每一次输出之后多打一个回车 ...
- Powerdesigner逆向工程从sql server数据库生成pdm (完整版)
第一步:打开"控制面板"中的"管理工具" 第二步:点击"管理工具"然后双击"数据源(odbc)" 第三步:打开之后,点击 ...
- Android_高仿建行圆形菜单
转:http://blog.csdn.net/lmj623565791/article/details/43131133 1.概述 今天打开建行看存款,一看伤心欲绝,再看:我擦,这个圆形菜单挺炫.于是 ...
- SQL Server 数据库状态选项
选项 1. single_user(单用户),multi_user(多用户),restricted_user(受限用户); 描述数据库的用户访问属性,它们互斥,设置其中任何一个选项就会取消对其它选项的 ...
- 打造坚固的安全的Linux服务器(ssh登录篇)
Nov 3 01:22:06 server sshd[11879]: Failed password for root from 123.127.5.131 port 38917 ssh2Nov ...
- ApiDemos示例学习(1)——ApiDemos示例的导入
---恢复内容开始--- 今天准备开始写这个ApiDemos示例的学习日记了,放在网上以监督自己! 首先是导入该示例.如果我们在配置Android开发环境是,利用Android SDK 安装包中的SD ...
- Linux软件大全
https://www.linux-apps.com/browse/cat/239/ord/latest/http://www.cnblogs.com/riskyer/p/3262745.htmlht ...
- 安装vmware tools失败解决方法
失败提示: reating a new initrd boot image for the kernel. update-initramfs: Generating /boot/initrd.img- ...
- vmware虚拟机迁移导致的eth0消失问题
将原来的ubuntu虚拟机文件迁移到还有一台机子之后. ifconfig显示仅仅有一个lo网卡,网上找了一些文章.大多是改动/etc/network/interfaces 原来内容是 # ###### ...