3.5.2. Типовые функции и заголовочные файлы
Большинство примеров программ включают заголовочный файл, содержащий необходимые в большинстве случаев определения, и в них также используется набор типовых функций. В этом разделе будут рассмотрены заголовочный файл и функции.
Типовой заголовочный файл
В листинге 3.1 приведен заголовочный файл, используемый практически в каждой программе, показанной в книге. Он включает различные другие заголовочные файлы, используемые во многих примерах программ, определяет тип данных Boolean и определяет макрос для вычисления минимума и максимума двух числовых значений. Применение данного файла позволяет немного сократить размеры примеров программ.
Листинг 3.1. Заголовочный файл, используемый в большинстве примеров программ
lib/tlpi_hdr.h
#ifndef TLPI_HDR_H
#define TLPI_HDR_H /* Предотвращает случайное двойное включение */
#include <sys/types.h> /* Определения типов, используемые
многими программами */
#include <stdio.h> /* Стандартные функции ввода-вывода */
#include <stdlib.h> /* Прототипы наиболее востребованных библиотечных
функций плюс константы EXIT_SUCCESS
и EXIT_FAILURE */
#include <unistd.h> /* Прототипы многих системных вызовов */
#include <errno.h> /* Объявление errno и определение констант ошибок */
#include <string.h> /* Наиболее используемые функции обработки строк */
#include "get_num.h" /* Объявление наших функций для обработки числовых
аргументов (getInt(), getLong()) */
#include "error_functions.h" /* Объявление наших функций обработки ошибок */
typedef enum { FALSE, TRUE } Boolean;
#define min(m,n) ((m) < (n)? (m): (n))
#define max(m,n) ((m) > (n)? (m): (n))
#endif
lib/tlpi_hdr.h
Функции определения типа ошибок
Чтобы упростить обработку ошибок в наших примерах программ, мы используем функции определения типа ошибок. Объявление такой функции показано в листинге 3.2.
Листинг 3.2. Объявление для наиболее востребованных функций обработки ошибок
lib/error_functions.h
#ifndef ERROR_FUNCTIONS_H
#define ERROR_FUNCTIONS_H
void errMsg(const char *format…);
#ifdef __GNUC__
/* Этот макрос блокирует предупреждения компилятора при использовании
команды 'gcc — Wall', жалующиеся, что "control reaches end of non-void
function", то есть что управление достигло конца функции, которая
должна вернуть значение, если мы используем следующие функции для
прекращения выполнения main() или какой-нибудь другой функции,
которая должна вернуть значение определенного типа (не void) */
#define NORETURN __attribute__ ((__noreturn__))
#else
#define NORETURN
#endif
void errExit(const char *format…) NORETURN;
void err_exit(const char *format…) NORETURN;
void errExitEN(int errnum, const char *format…) NORETURN;
void fatal(const char *format…) NORETURN;
void usageErr(const char *format…) NORETURN;
void cmdLineErr(const char *format…) NORETURN;
#endif
lib/error_functions.h
Для определения типа ошибок системных вызовов и библиотечных функций используются функции errMsg(), errExit(), err_exit() и errExitEN().
#include "tlpi_hdr.h"
void errMsg(const char *format…);
void errExit(const char *format…);
void err_exit(const char *format…);
void errExitEN(int errnum, const char *format…);
Функция errMsg() выводит сообщение на стандартное устройство вывода ошибки. Ее список аргументов совпадает со списком для функции printf(), за исключением того, что в строку вывода автоматически добавляется символ конца строки. Функция errMsg() выводит текст ошибки, соответствующий текущему значению переменной errno. Этот текст состоит из названия ошибки, например EPERM, дополненного описанием ошибки в том виде, в котором его возвращает функция strerror(), а затем следует вывод, отформатированный согласно переданным агрументам.
По своему действию функция errExit() похожа на errMsg(), но она также прекращает выполнение программы, либо вызвав функцию exit(), либо, если переменная среды EF_DUMPCORE содержит непустое строковое значение, вызвав функцию abort(), чтобы создать файл дампа ядра для его использования отладчиком. (Файлы дампа ядра будут рассмотрены в разделе 22.1.)
Функция err_exit() похожа на errExit(), но имеет два отличия:
• не сбрасывает стандартный вывод перед выводом в него сообщения об ошибке;
• завершает процесс путем вызова _exit(), а не exit(). Это приводит к тому, что процесс завершается без сброса буферов stdio или вызова обработчиков выхода.
Подробности этих различий в работе err_exit() станут понятнее при изучении главы 25, где рассматривается разница между _exit() и exit(), а также обработка буферов stdio и обработчики выхода в дочернем процессе, созданном с помощью fork(). А пока мы просто возьмем на заметку, что функция err_exit() будет особенно полезна при написании нами библиотечной функции, создающей дочерний процесс, который следует завершить по причине возникновения ошибки. Это завершение должно произойти без сброса дочерней копии родительских буферов stdio (то есть буферов вызывающего процесса) и без вызова обработчиков выхода, созданных родительским процессом.