《C陷阱与缺陷》读书笔记5

来源:互联网 发布:手机淘宝怎样买火车票 编辑:程序博客网 时间:2024/05/22 10:37
 

第五章 库函数

 

5.1  返回整数的getchar函数

 

        #include <stdio.h>

        Main ()

        {

            char c;

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

                putchar(c);

        }

        这个程序乍看似乎是把标准输入复制到标准输出,实则不然。原因在于程序中的变量c被声明为char类型,而不是int类型。这意味着c无法容下所有可能的字符,特别是,可能无法容下EOF。

 

5.2  更新顺序文件

 

        一个输入操作不能随后直接跟一个输出操作,反之依然。如果要同时进行输入和输出操作,必须在其中插入fseek函数的调用。

 

        下面的程序片段似乎更新了一个顺序文件中选定的记录:

        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);

            }

        }

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

        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);

           }

        }

        第二个fseek函数虽然看上去什么也没有做,但是它改变了文件的状态,使得文件现在可以正常地进行读取了。

 

5.3  缓冲输出与内存分配

 

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

        这种控制能力一般是通过库函数setbuf实现的。如果buf是一个大小适合的字符数组,那么setbuf (stdout, buf); 语句将通知输入/输出库,所有写入到stdout的输出都应该使用buf作为输出缓冲区,知道buf缓冲区被填满或者程序员直接调用fflush,buf缓冲区中的内容才实际写入到stdout中。缓冲区的大小由系统头文件<stdio.h>中的BUFSIZ定义。

 

        #include <stdio.h>

        Main()

        {

           int c;

           char buf[BUFSIZ];

           setbuf(stdout,buf);

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

                putchar(c);

        }

        这个程序是错误的。程序中对库函数setbuf的调用,通知了输入/输出库所有字符的标准输出应该首先缓冲在buf中。

        buf缓冲区最后一次被清空是在main函数结束之后,作为程序交由控制给操作系统之前C运行时库所必须进行的清理工作的一部分。但是,在此之前buf字符数组已经被释放。

        要避免这种类型的错误有两种方法。第一种方法是让缓冲数组成为静态数组,既可以直接显示声明buf为静态:static char buf[BUFSIZ];

        也可以把buf完全移到main函数之外。第二种方法是冬天分配缓冲区,在程序中兵部主动释放分配的缓冲区。(由于缓冲区是动态分配的,所以main函数结束时病不会释放该缓冲区,这样C运行时库进行清理时就不会发生缓冲区已释放的情况):

        char *malloc();

        setbut(stdout, malloc(BUFSIZ));

        这里并不需要检查malloc函数调用是否成功。如果失败,将返回一个null指针。Setbuf函数的第二个参数取值可以为null,此时标准输出不需要进行缓冲。

 

5.4  使用errno检测错误

 

        很多库函数,特别是那些与操作系统有关的,当执行失败时会通过一个名称为errno的外部变量,通知程序该函数调用失败。

 

        /* 调用库函数 */

        if (errno)

           /* 处理错误 */

        上述代码出错的原因在于,在库函数调用没有失败的情况下,并没有强制要求库函数一定要设置errno为0,这样的errno的值就可以是前一个执行失败的库函数设置的值。

 

        errno = 0;

        /* 调用库函数 */

        if (errno)

            /* 处理错误 */

        还是错误,因为库函数在调用成功时,既没有强制要求对errno清零,但同时也没有禁止设置errno。

 

        因此,在调用库函数时,应该首先检测作为错误指示的返回值,确定程序执行已经失败。然后,再检查errno,来搞清楚出错原因:

        /* 调用库函数 */

        if (返回的错误值)

           /* 检查errno */

 

5.5  库函数signal

原创粉丝点击