《UNIXLinux程序设计教程》一1.6 错误处理

1.6 错误处理

UNIX的系统调用和大部分库函数在失败时会返回一特殊值报告出错,这个特殊值通常是–1。这种返回值仅告诉调用遇到了错误而已,为了让应用知道究竟发生了什么错误,系统调用和库函数同时还会在系统定义的变量errno中给出指明错误原因的错误码。
变量errno是系统调用和库函数用来报告错误的一种标准方法。早期的UNIX和C将它实现为外部整型变量,其说明为:

extern int errno;

这是进程中的全局共享变量,无法支持多线程。为了支持多线程,新的POSIX标准和C标准在要求这个变量为整型左值的同时,还规定一个线程对errno的读写不受其他线程更改errno的影响。因此,新的UNIX和C实现已经改变为每个线程各自有自己对应的errno。例如,Linux将其定义为:

extern int *__errno_location(void);
#define errno (*__errno_location(void))

这样,每个线程便可通过内建的专门函数获得各自errno的存储单元。
头文件定义了变量errno以及它可以取值的错误码。每一种错误码有一个符号名,它们是定义在中以字母'E'开头的宏名字。所有错误码的值都是正整数且除了EWOULDBLOCK和EAGAIN之外每一个都互不相同。因此,可以用它们作为switch语句内的case标号来区分错误情形。表1-1列出了一些常见的错误码。
程序开始时,变量errno的初值一定是0,UNIX的系统调用和许多库函数当遇到错误时均保证设置该变量为某个非0值。errno的值只有在函数调用出错时才被设置,函数调用成功则不会改变,它可能是前一次调用某个函数出错时的值。因此,不应当用errno来检测一个调用是否失败。正确的做法是仅当函数的返回值指出这个变量被明显设置时才用errno来确定错误原因。

不会有任何函数设置errno的值为0来指出错误,errno的这个性质可以用来检测某些特殊函数的错误返回值。有少数函数,如1.8.3节介绍的pathconf(),在出现错误的情况下仍返回一个合法值,但同时也设置errno。对于这些函数,如果我们想检测是否遇到了错误,应当在调用它们之前置errno为0,函数调用之后再检查其值。我们在程序1-3中会见到这种情况的例子。
errno的错误码是整数,C标准函数strerror()可将错误码转换为可读的报错信息。

#include <string.h>
char *strerror(int errnum);
 

strerror()返回与errnum错误码相对应的错误信息字符串。我们不能修改由strerror()返回的字符串,同样,如果紧接着再次调用strerror(),前一次调用得到的字符串将被覆盖。
如果想直接将errno当前值对应的错误信息输出到标准报错文件,则可调用perror()。

#include <stdio.h>
void perror(const char *msg); 

perror()首先打印msg指定的信息,后随一个冒号和空格,然后打印与errno对应的错误字符串。
如果msg是空指针或指向空字符串,perror()打印的将是与strerror()完全相同的信息,但perror()附加有换行符,而strerror()没有。
例1-1 当系统调用或库函数发生错误时,在有些情况下会需要立即终止程序的运行,因为继续执行已没有意义。此时,可以用perror()或strerror()这两个函数之一打印出对应的错误信息,然后调用exit(1)终止程序的执行。本书的很多例子就是这种情况。为此,我们统一使用一个函数err_exit()来完成报告错误并退出的动作。这个函数以宏方式定义在我们的头文件err_exit.h(程序1-1)中,其中,宏调用参数MESSAGE给出用户需要输出的信息。

本书在介绍每一个函数时,有时会省略返回值的说明,此时除非特殊说明,均遵循UNIX的一般约定:调用成功的返回值是0,错误返回值是–1,并设置了错误码于errno中。

时间: 2024-10-27 01:46:50

《UNIXLinux程序设计教程》一1.6 错误处理的相关文章

《UNIXLinux程序设计教程》一导读

前言 十年前,我们出版了<UNIX程序设计教程>(清华大学出版社).十年来,影响UNIX编程接口的规范和标准发生了较大变化,当时写书参照的"Single UNIX Specification 2"现在已发展到了"Single UNIX Specification 4",而若干分离独立的规范和标准,包括Single UNIX Specification,现在都已经统一在POSIX.1-2008标准之下.同时,随着Linux系统的成熟和发展,UNIX系统已不

《UNIXLinux程序设计教程》一2.6 文件结束和错误指示器

2.6 文件结束和错误指示器 本章的多数函数(fgets().gets().putc().ungetc().fread()等)当调用不成功时都返回EOF,EOF的值为0,它既用于报告文件结束,也用于报告错误情形下的返回.因此,为了区分究竟是错误返回还是文件结束返回,就需要调用ferror()函数来确定是否存在错误,调用feof()函数检查是否遇到文件结束.每一个流对象内部有两个指示器:一个为错误指示器,当读写文件出错时该指示器被设置:另一个为文件结束指示器,当遇到文件尾时该指示器被设置.函数fe

《UNIXLinux程序设计教程》一第1章 UNIX导论

第1章 UNIX导论UNIX是一个"历史悠久"的操作系统.在开始讲述UNIX环境程序设计方法之前,我们先回顾UNIX的诞生.成长和发展历程,介绍UNIX发展过程中出现的若干标准.回顾UNIX的发展历史,有助于我们了解它具有如此强大生命力的原因,并把握它未来的发展方向:了解UNIX的标准,可以使我们理解和区分UNIX的不同实现与版本之间的区别,并编写出可移植性更好的程序.随后,作为后继章节的基础,本章将讲述UNIX的一些基本概念,并介绍与UNIX全系统有关的一些内容,例如系统信息.系统能

《UNIXLinux程序设计教程》一2.8 格式I/O

2.8 格式I/O 前几节介绍的流I/O函数除了以字符或行方式进行读写外,并不对数据进行解释,但在很多时候应用都会需要对输入输出数据进行解释,因为数据在计算机内的表示和人们可读的形式是不同的.数据在计算机内是二进制形式,在计算机外部常常为正文形式.例如,十进制数12在计算机内部的32位二进制表示是:00000000000000000000000000001100.当这个数在打印机上输出或者在终端屏幕上显示时,必须转换为字符'1'和'2',它们的ASCII编码分别为00110001和0011001

《UNIXLinux程序设计教程》一1.4 系统库

1.4 系统库 系统库给应用程序提供编译好的标准函数和系统调用函数的目标代码,这些代码在连接时与应用程序的目标代码装配在一起形成一个完整的可执行程序.UNIX系统库由许多专门的库组成,如C标准库.数学库.线程库.实时库等.本书将要介绍的函数和系统调用基本上都包含在系统库的C标准库中,也有部分包含在线程库和实时库中. C标准库不仅包含了C标准规定的函数(不包括科学计算函数以及国际化和宽字符函数),而且包含了POSIX标准定义的大部分编程接口函数.当我们编译C源程序时,编译器会自动地连接C标准库.但

《UNIXLinux程序设计教程》一2.5 文件定位

2.5 文件定位 读写文件过程中,有时会需要读某个特定位置的内容.例如,对于那种由固定大小的记录组成并能用整数索引来引用这些记录的文件,为访问其中某个特定的记录,最快捷的方法是直接定位至该记录位置进行读写,而不必一个一个地顺序跳过之前不需要的记录.为此,我们需要能够随意定位文件的位置,即随机地读写文件的任何部分. 标准I/O库提供了如下两组对随机文件进行定位的函数,用它们可以随机地读写文件的任何部分. #include <stdio.h> long int ftell(FILE *stream

《UNIXLinux程序设计教程》一2.7 流缓冲

2.7 流缓冲 每一个流都有一个输入输出缓冲区.写入流的字符并不立即写到文件中,而是先在缓冲区中聚集为一块,然后异步地以块为单位传送到文件.类似地,从流读出的字符也不是逐个地从文件中读出,而是以块为单位从文件读到缓冲区,然后从缓冲区传送给进程.这种处理方式称为缓冲. 采用缓冲的目的是为了减少调用低级I/O函数(如read()和write())的次数,因为这些真正读写文件的函数是系统调用,它们是较费时间的操作.例如,对于存储在硬盘上的文件,当进程用read()或write()读写数据时,设备驱动程

《UNIXLinux程序设计教程》一3.7 非阻塞I/O

3.7 非阻塞I/O 前面几节已介绍了完成各种I/O的系统调用,如read().write().open()等,这些系统调用在默认情形下均是阻塞的,也就是说,调用必须等待操作完成,即读写到数据,才能返回.但在有些应用中往往还有需要非阻塞I/O的情形.本节我们讨论使得这些调用成为非阻塞的方法. UNIX系统调用根据阻塞还是非阻塞分为两类:一类是所谓的"慢"系统调用,其他的则归为另一类.慢系统调用是以下有可能被永久阻塞的调用: 调用read()读管道.终端设备或网络设备文件时,如果数据不出

《UNIXLinux程序设计教程》一2.1 UNIX 输入输出基本概念

2.1 UNIX 输入输出基本概念 在任何一种操作系统中,程序开始读写一个文件的内容之前,必须首先在程序与文件之间建立连接或通信通道,这一过程称为打开文件.打开一个文件的目的可能是要读其中的数据,也可能是要往其中写入数据,还可能是既要读又要写数据. UNIX系统有两种机制用于描述程序与文件的这种连接:一种称为文件描述字,另一种称为流.因此,系统中关于I/O的函数也分为两大类:一类针对文件描述字操作,另一类针对流操作. 当用流或描述字I/O函数打开一个文件时,它们分别返回一个流或文件描述字,然后便