Chinaunix首页 | 论坛 | 博客
  • 博客访问: 631230
  • 博文数量: 87
  • 博客积分: 3399
  • 博客等级: 中校
  • 技术积分: 1422
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-17 21:20
文章分类

全部博文(87)

文章存档

2013年(1)

2012年(51)

2011年(33)

2010年(2)

分类: C/C++

2012-03-09 10:38:02

5.1返回整数的getchar函数

我们来看下面的程序:

#include "stdio.h"

void main()

{

    char c;

    while ( (c = getchar()) != EOF )

    {

        putchar( c );

    }

}

看起来这段程序应该把它的标准输入拷贝到输出,实际运行情况并非如此。

原因在于 c 声明为 char 型而非 int 型,这就意味着 c 不能容下所有可能的字符,特别是可能无法容下EOF

所以,有两种可能:或者某些合法字符被“截断”后,使得c的取值与EOF相同,此时程序在文件复制中途终止;或者 c 根本不可能捕捉到EOF程序将进入死循环

实际上,还有第三种情况,程序将随机运行。C 参考手册非常严格地定义了表达式 ( (c = getchar()) != EOF ) 的值。6.1节指出:当一个长整型数据被转换为短整型或字符型时,左端被删掉,多余的位被简单的忽略。7.14节指出:所有赋值操作符的组合方向都是从右至左。它们都要求有一个左值,并且赋值表达式的类型就是它的左操作数的类型。表达式的值就是赋值后的左操作数的值。

综合以上两点看,getchar( ) 的值要被删掉高位数据,删节后的值再与EOF相比。作为比较的一部分,c 必须要扩展为整型,扩展时左端或者补 0 ,或者补符号位,由具体情况而定。

有些编译器将不能正确地执行这个表达式。它们可能把 getchar( ) 的低位数据赋给 c 。但是,它们不是比较 c EOF,而是比较 getchar( ) EOF。如果编译器这样做,将会同样只是看起来工作正常

5.2更新(同时读写)顺序文件

         FILE *file;

         fopen(filePath,"r+");

执行上述代码后,并不能对文件进行交替的读和写。一个输入不能同时紧跟一个输出,反之亦然。如果要同时进行输入和输出操作,必须在其中插入fseek的操作。

         FILE *file;

         fp = fopen(filePath,"r+");

         struct record rec;

         //freadfp中读取1个大小为sizeof(rec)的对象到&rec所指向的数组中,返回读取的内容数量

         while(fread((char*)&rec, sizeof(rec),1,fp) == 1)

         {

                   /*rec执行某些操作*/

                   if(/*rec必须被重新写入*/)

                   {

                            //fseek为流fp设置位置,即当前位置之后的sizeof(rec)

                            //使用fseek可以清除关联到流的EOF标记

                            //fseek要求第二个参数为long,sizeof返回unsigned

                            fseek(fp,-(long)sizeof(rec), 1);

                            //&rec中读取1个大小为sizeof(rec)的对象到fp

                            fwrite( (char*)&rec, sizeof(rec),1,fp);

                   }

         }上述代码出错,因为fwrite执行后,再执行fread,则出错。需在fwrite后加如下语句:

fseek(fp,0L,1);

缓冲输出和内存分配

int main()

{

         int c;

         char buf[BUFSIZ]; //BUFSIZE定义

         //所有输入到stdout中的输出都应该先输入到buf

         //buf满时或显示调用fflush时,才将缓冲区中的内容写入stdout

         setbuf(stdout,buf); 

         while((c=getchar())!=EOF)

         {

                   putchar(c);

         }

         return 0;

}

上面程序是错误的,因为程序将控制交给操作系统前c运行库会将buf清空,但在这之前buf字符数组已被释放。

解决方法:

一:buf声明为静态static char buf[BUFSIZE];或者将buf声明放到main之外;

二:动态分配缓冲区,程序中并不主动释放缓冲区。因为是动态分配的,所以main函数结束时并不释放缓冲区。

char *malloc(); setbuf(stdout,malloc(BUFSIZ));

这种情况下,没有必要去判断 malloc( ) 是否成功,因为如果失败了,它会返回一个空指针。空指针可以作为 setbuff( ) 的第二个参数,它意味着不被缓冲。这运行起来会比较慢,但它会正常运行。

5.4使用errno检测错误

很多库函数在执行失败时会通知一个名字为errno的外部变量,通知程序该函数调用失败

/*调用库函数*/ if(errno) /*处理错误*/

这样做是错误的,因为库函数在调用成功时并强制要求将errno设置为0,这样errno就可能是前一个库函数执行失败时的值。

errno = 0; /*调用库函数*/ if(errno) /*处理错误*/

这行代码还是错的。例如当fopen被要求创建一个新的文件供程序输出时,如果存在一个同名文件,fopen会先删除它,然后新建一个文件。这样fopen会调用另一个库函数检测是否有同名文件存在。假设用于检测同名文件的函数在文件不存在时会设置errno,则fopen每创建一个事先并不存在的文件时,即使程序没错,errno也会被设置

解决方法:

在调用库函数时,先检测作为错误提示的返回值,再检测errno找出错误原因

/*调用库函数*/ if(返回的错误值) 检测errno /*处理错误*/

5.5库函数signal

singal中声明。signal(signal type,handler function);

signal type 代表系统头文件signal.h中定义的常量,标示要捕获的信号类型。Handler function 为事件发生时要加以调用的事件处理函数。

Malloc时调用signal,从signal中使用longjmp退出,都是不安全的。

信号非常复杂棘手,而且具有一些在本质上不可移植的特性。因此应该使signal处理函数尽可能的简单,并将它们组织在一起,将来可以很容易修改。

Exercise 5.1

当一个程序异常终止时,程序输出的最后几行通常会丢失,什么原因?解决办法?


 

 

Answer:当程序使用输出缓冲区时,如果程序异常终止,可能还没来得及清空缓冲区,此时这些输出可能会存在内存而不被写出了。这种情况会给调试这类程序的编程者造成一种假象,程序发生失败的时刻比实际上运行失败的真正时刻要早得多。

解决方法:在调试时强制不允许对输出进行缓冲:setbuf(stdout, (char*)0);

这一句必须在任何输出被写入到stdout之前执行。最好作为 mian函数的第一句。

Exercise 5.2

#include

int main()

{

         register int c;

         while((c=getchar())!=EOF)

                   putchar(c);

         return 0;

}

#define EOF -1

int main()

{

         register int c;

         while((c=getchar())!=EOF)

                   putchar(c);

         return 0;

}

右边的程序在某些系统中仍然可运行,但运行的慢,原因?

Answer:函数调用需要较大的开销,因此getcharputchar常被在stdio.h中定义为宏,同时很多c语言的实现在库文件中也包含这两个函数。当不包含头文件时,使用的是库函数getchar而不是getchar宏,所以程序变慢。

 

阅读(1209) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~