• C语言缺陷与陷阱读书笔记(五)


    库函数

    5.1 返回整数的getchar函数###

    #include <stdio.h>
    
    int main(void)
    {
        char c;
        while ((c= getchar())!=EOF)
    }
    

    字符赋值函数,由于c被定义为char类型,c不会接收到EOF这个值。因此程序会进入死循环,无法正常退出。

    扩展知识:

    EOF(End of File),即文件结束符.
    其定义为:#define EOF (-1).也就是说EOF代表一个整型常量-1,但一般文件的末尾不存在这个结束标识,那它又是如何判断是否到达文件末尾的呢?

    1.对于文本文件来说:由于函数读取数据时是不会返回结果为-1的负数的,只有当没有数据可以读取时才会返回EOF,因此可以正常工作.

    2.对于二进制文件而言,如果读取的数据为0xFF的话,此时返回值为-1,即EOF,但是这个时候又没有到达文件末尾,造成数据读取读取出现中途中断.

    那如果判定二进制文件是否到达文件末尾?

    1.在进行数据读取时,将返回的字符型数据定位为整型
    若ch为char型,则当将返回值0x 00 00 00 FF返回时,取低8位赋给ch,那么此时ch为-1,此时会误判为到达文件末尾;

    而若ch为int型,则当将返回值0x 00 00 00 FF返回时,ch的值为0x 00 00 00 FF,此时ch不为-1,不会误判为文件末尾。

    (当然上面所述成立必须是在读取不出错的情况下才成立)

    2.使用feof

    其函数原型为:
    int feof(FILE *fp),若到达文件末尾则返回一个非零值,否则返回0

    #define _IOEOF          0x0010
    
    #define feof(_stream)     ((_stream)->_flag & _IOEOF)
    

    可知feof函数判断是否到达文件末尾时与_flag这个标志有关。

    5.2 更新顺序文件

    #include<stdio.h>
    
    int main(void)
    {
    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);
            fseek(fp,0L,1);
    
        }
    }
    }
    
    //上面文件读取写入程序会报错,由于fread与fwrite函数之间的调用状态改变需要fseek函数进行
    //读写之间的状态改变,因此需要在fwrite函数添加一行代码:fseek(fp,0L,1)
    

    5.3缓冲输出与内存分配

    程序输出有两种方式:一种是即时处理方式,另一种是先暂存起来,然后再大块写入的方式,前者往往造成较高的系统负担。因此,c语言实现通常都允许程序员进行实际的写操作之前控制产生的输出数据量。

    这种控制能力一般是通过库函数setbuf实现的。如果buf是一个大小适当的字符数组,那么:

    setbuf(stdout,buf);
    

    语句将通知输入/输出库,所有写入到stdout的输出都应该使用buf作为输出缓冲区,直到buf缓冲区被填满或者程序员直接调用fflush(译注:对于由写操作打开的文件,调用fflush将导致输出缓冲区的内容被实际地写入该文件),buf缓冲区中的内容才实际写入到stdout中。缓冲区的大小由系统头文件<stdio.h>中的BUFSIZ定义。BUFSIZ的缺省值为512.

    如果需要在调试时强制不允许对输出进行缓冲,可在在printf函数之前写入一下代码:

    setbuf(stdout,(char *)0);
    

    5.4使用errno检测错误###

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

    上述代码是错误的,由于在库函数调用没有失败的情况下,并不会强制设置errno的值。这样errno的值可能是前一个执行失败的库函数设置的值。

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

    上述代码也是错误的,库函数在调用成功时,既没有强制要求对errno清零,也没有禁止设置errno。

    正确使用errno检测错误的方法如下:

    //调用库函数
    if (返回的错误值)
                检查errno
    

    5.5库函数signal###

    signal库函数是捕获异步事件的一种方式。使用该库函数首先在源文件加上:

    #include <signal.h>
    

    以引入相关的声明。处理特定的signal(信号)时需要如此调用:

    signal (signal type, handler function);
    

    signal type代表系统头文件signal.h中定义的某些常量,这些常量用来标识signal函数将要捕获的信号类型,handler function 表示当制定的事件发生时,将要加以调用的事件处理函数。

    从安全的角度来讲,信号的处理函数不应该调用上述类似malloc的库函数。
    signal处理函数的唯一安全、可移植的操作就是打印一条出错消息,然后使用longjump或者exit立刻退出程序。

  • 相关阅读:
    C# 中的类型转换
    Structured Query Language 入门 oracle
    C# 模板代碼的總結
    .net 頁面通過C#控件綁定時間格式的方法
    醫務室系統報表中使用的一個使用遊標的自定義方法 sqlserver
    vi 编译器的退出
    和为s的数字
    两个链表的第一个公共节点
    某数字在排序数组中出现的次数
    二叉搜索树的第k个节点
  • 原文地址:https://www.cnblogs.com/luyoujun/p/4828655.html
Copyright © 2020-2023  润新知