本帖最后由 倾心莫若初见 于 2016-10-21 16:21 编辑
我们知道,C语言中没有定义输入/输出语句,任何一个有用的C程序(起码必须接受零个或多个输入,生成一个或多个输出)都必须调用库函数来完成最基本的输入/输出操作.大多数库函数的使用都不会有什么问题,它们的意义和用法明白而直接,基本上都能正确的使用它们,但也有一些例外情况,如一些常用到的库函数表现出来的行为方式往往有悖于使用者的本意,今天我们就来讨论一些常用到的库函数,以及使用过程中可能出错之处.
1. 返回整数的getchar 函数 我们先来看一个简单的例子:
# include <stdio.h> int main(void) { char c; while((c = getchar())!= EOF) putchar(c); return 0; } getchar 函数在一般情况下返回的是标准输入文件中的下一个字符,当没有输入时返回EOF,这个程序看似是把标准输入复制到标准输出,实则不是,原因在于程序中的变量c 被声明为char 类型,而不是int类型,这意味着无法容下所有的字符,特别是无法容下EOF. 因此,最终结果有两种可能,一种是,某些合法的输入字符在被截断后是的c的取值与EOF 相同,另一种是,c 根本不可能取到EOF这个值,对于前一种情况,程序在文件复制的中途终止;对于后一种情况,程序将陷入一个死循环. 实际上,还存在了第三种情况,程序表面上似乎能够正常工作,那是因为巧合.尽管函数getchar的返回记过在赋给char类型的变量c时会发生截断操作.将返回值的地位数据赋给c,但与EOF 的比较不是c,而是getchar的返回值,编译器如果采用这种做法,上面的程序就可以正常运行了.
2. 跟新顺序文件 许多系统中的标准输入/输入库都允许程序打开一个文件,同时进行写入和读取的操作. FILE * fp; fp = fopen(file,”r+”); 上面的例子代码打开了文件名由变量file指定的文件,对于存取权限的设定表明程序希望对这个文件进行输入和输出操作. 编程者也许认为,程序一旦执行上述操作完毕,就可以自由的交错进行读出和写入的操作.但事实上并不是我们想象的那样,我们在进行了读操作或者写操作之后,都会改变文件指针的位置,因此,先要同时进行输入和输出操作,必须使用fseek 函数改变文件指针的位置.
3. 使用errno 检测错误 很多库函数,特别是与操作系统相关的,当执行失败时会通过一个名称为 errno的外部变量,通知程序该函数调用失败.请看下面的额例子: /*调用库函数*/ if (errno) /*处理错误*/ 这是一个简单的错误处理程序,看起来没问题,然而却是错误的.出错原因在于,在库函数调用没有失败的情况下,并没有强制要求库函数一定设置errno为0,这样errno 的值就可能是前一个执行失败的库函数设置的值.我们对代码做了一下调整,看似正确,但可能还是错误的: Errno = 0; /*调用库函数*/ if(errno) /*出错处理*/ 原因在于,库函数在调用成功后,即没有强制要求对errno清零,同时也没有禁止设置errno.因为在调用一个函数的时候,可能会在内部调用另一个函数,此时可能设置errno. 故,在调用库函数时,我们应该首先检测作为错误指示的返回值,确定程序已经执行失败,然后,在检查errno,来搞清楚出错原因: /* 调用库函数 */ if(返回的错误值) 检查errno 4. 库函数signal 实际上,所有的c 语言实现中都包括有signal库函数,作为捕获异步事件的一种方式.要使用该库函数,需要在源文件中加上 #include <signal.h> 以引入先关的声明,要处理一个特定的signal(信号),可以这样调用signal函数: signal(signal type, handle function); signal type 代表要处理的信号.handle function代表信号发生时,调用的事件处理函数.在许多c语言实现中,信号是真正意义上的”异步”.从理论上说,一个信号可能在C程序执行期间的任何时刻发生,信号甚至可能出现在某些复杂的库函数(如malloc)的执行过程中.因此,信号的处理函数不应该调用上述类型的库函数. 我们需要让signal处理函数尽可能的简单,并将它们组织在一起,这样,当需要适应一个系统时,我们可以很容易的进行修改.
今天的讲解就到这里了,想要了解或者学习更多、更详细的内容,欢迎来到我们黑马程序员学习。
|