9.7. Обработка ошибок: stderr и exit
Обработка ошибок в программе cat неидеальна. Неудобство заключается в том, что если один из файлов по некоторой причине оказывается недоступным, диагностическое сообщение об этом печатается в конце объединенного вывода. Это приемлемо, если вывод поступает на терминал, но не годится, если вывод поступает в некоторый файл или через поточный (pipeline) механизм в другую программу.
Чтобы лучше обрабатывать такую ситуацию, к программе точно таким же образом, как stdin и stdout, присоединяется второй выходной файл, называемый stderr. Если это вообще возможно, вывод, записанный в файле stderr, появляется на терминале пользователя, даже если стандартный вывод направляется в другое место.
Пример 9-5. Давайте переделаем программу cat таким образом, чтобы сообщения об ошибках писались в стандартный файл ошибок.
// cat: Объединить (от англ. concatenate) файлы
#include <stdio.h>
main(int argc, char *argv)
{
FILE *fp, *fopen();
if(argc==1) // Нет аргументов: копировать stdin
filecopy(stdin);
else
while (--argc > 0)
if((fp=fopen(*++argv,"r"))== NULL)
{
printf(stderr, "cat: не открыть %s\n", argv);
exit(1);
}
else
{
filecopy(fp);
}
exit(0);
}
Программа сообщает об ошибках двумя способами. Диагностическое сообщение, выдаваемое функцией fprintf, поступает в stderr и, таким образом, оказывается на терминале пользователя, а не исчезает в потоке (pipeline) или в выходном файле.
Программа также использует функцию exit из стандартной библиотеки, обращение к которой вызывает завершение выполнения программы. Аргумент функции exit доступен любой программе, обращающейся к данной функции, так что успешное или неудачное завершение данной программы может быть проверено другой программой, использующей эту в качестве подзадачи. По соглашению величина 0 в качетсве возвращаемого значения свидетельствует о том, что все в порядке, а различные ненулевые значения являются признаками нормальных ситуаций.
Функция exit вызывает функцию fclose для каждого открытого выходного файла, с тем чтобы вывести всю помещенную в буферы выходную информацию, а затем вызывает функцию _exit. Функция _exit приводит к немедленному завершению без очистки каких-либо буферов; конечно, при желании к этой функции можно обратиться непосредственно.
Стандартная библиотека содержит функцию fgets, совершенно аналогичную функции getline, которую мы использовали на всем протяжении книги. В результате обращения fgets(line, maxline, fp) следующая строка ввода (включая символ новой строки) считывается из файла fp в символьный массив line; самое большое maxline_1 символ будет прочитан. Результирующая строка заканчивается символом \0. Нормально функция fgets возвращает line; в конце файла она возвращает NULL. (Наша функция getline возвращает длину строки, а при выходе на конец файла – нуль).
Предназначенная для вывода функция fputs записывает строку (которая не обязана содержать символ новой строки) в файл:
fputs(line, fp) .
Пример 9-6. Чтобы показать, что в функциях типа fgets и fputs нет ничего таинственного, мы приводим их ниже, скопированными непосредственно из стандартной библиотеки ввода-вывода:
// fgets: получить не более n символов из файла iop
#include <stdio.h>
char *fgets(char *s, int n, register FILE *iop)
{
register int c;
register char *cs;
cs = s;
while(--n>0&&(c=getc(iop)) !=EOF)
if ((*cs++ = c)=='\n')
break;
*cs = '\0';
return((c== EOF && cs==s) 7 NULL : s);
}
// fputs: посылает строку в файл iop
fputs(register char *s, register FILE *iop)
{
register int c;
while (c = *s++)
putc(c,iop);
}
Упражнение 8-3. напишите программу сравнения двух файлов, которая будет печатать первую строку и позицию символа, где они различаются.
Упражнение 8-4. Переделайте программу поиска заданной комбинации символов из главы 6 таким образом, чтобы в качестве ввода использовался набор именованных файлов или, если никакие файлы не указаны как аргументы, стандартный ввод. Следует ли печатать имя файла при нахождении подходящей строки?
Упражнение 8-5. Напишите программу печати набора файлов, которая начинает каждый новый файл с новой страницы и печатает для каждого файла заголовок и счетчик текущих страниц.
9.9. Несколько разнообразных функций
Стандартная библиотека предоставляет множество разнообразных функций, некоторые из которых оказываются особенно полезными. Мы уже упоминали функции для работы со строками: strlen, strcpy, strcat и strcmp. Вот некоторые другие.
9.9.1. Проверка вида символов и преобразования. Некоторые макросы выполняют проверку символов и преобразования. Возврат ненулевого значения – это true (истина), а ненулевого значения – false (ложь). Например, для переменой int n справедливо:
;
;
;
;
;
.
Кроме того, существуют две полезные функции:
int n = oupper(c) – преобразует букву c в прописную;
int n = olower(c) – преобразует букву c в строчную.
Такие функции мы уже научились создавать сами.
9.9.2. Функция ungetc. Стандартная библиотека содержит довольно ограниченную версию функции ungetch, написанной нами в главе 5 она называется ungetch. В результате обращения:
ungetc(c,fp)
символьная переменная c возвращается в файл fp. Позволяется возвращать в каждый файл только один символ. Функция ungetc может быть использована в любой из функций ввода и с макросами типа scanf, getc или getchar.
9.9.3. Обращение к системе. Функция system(s) выполняет команду, содержащуюся в символьной строке s, и затем возобновляет выполнение текущей программы. Содержимое s сильно зависит от используемой операционной системы. В качестве тривиального примера, укажем, что на системе Unix строка:
system("date");
приводит к выполнению программы date, которая печатает дату и время дня.
9.9.4. Управление памятью. Функция calloc весьма сходна с функцией alloc, использованной нами в предыдущих главах. В результате обращения:
calloc(n, sizeof(objcct))
возвращается либо указатель пространства, достаточного для размещения n объектов указанного размера, либо NULL, если запрос не может быть удовлетворен. Отводимая память инициализируется нулевыми значениями.
Указатель обладает нужным для рассматриваемых объектов выравниванием, но ему следует приписывать соответствующий тип, как в:
char *calloc();
int *ip;
ip=(int*) calloc(n,sizeof(int));
Функция cfree(p) освобождает пространство, на которое указывает p, причем указатель p певоначально должен быть получен в результате обращения к calloc. Здесь нет никаких ограничений на порядок освобождения пространства, но будет неприятнейшей ошибкой освободить что-нибудь, что не было получено обращением к calloc.
Данные функции управления памятью есть в составе операционной системы. Однако, язык C++ предоставляет удобный оператор new, который обеспечивает то же самое. Реализация программы распределения памяти, подобной calloc, в которой размещенные блоки могут освобождаться в произвольном порядке, продемонстрирована в главе 8 с помощью операторов new и sizeof.
1. Болски М.И. Язык программирования Си: Справочник. – М.: Радио и связь, 2000. – 96 с.
2. Керниган Б., Пайк Р. Практика программирования. – СПб.: Невский диалект, 2001. – 381 с.
3. Керниган Б., Ритчи Д. Язык программирования Си. – СПб.: Невский диалект, 2001 + М.: Финансы и статистика, 2001. – 352 с.
4. Кнут Д. Искусство программирования. Том 1. – М.: Статистика, 1975. – 568 с.
5. Круглински Д. Дж., Уингоу С., Шеферд Дж. Программирование на Microsoft Visual С++ 6.0 для профессионалов. – СПб.: Питер; М.: Русская редакция, 2001. – 864 с.
6. Крупник А.Б. Изучаем Си. – СПб.: Питер, 2002. – 256 с.
7. Крупник А.Б. Изучаем С++. – СПб.: Питер, 2002. – 251 с.
8. Шилдт Г. Программирование на С и С++ для Windows. – К.: Торгово-издательское бюро BHV, 2001. – 408 с.
* См. А. Крупник «Изучаем C++». – Глава 1. Что такое программирование. – СПб.: Питер, 2003. –
251 с.
5 Слова «ячейка» и «байт» обозначают одно и то же – восемь последовательных битов.
* Именно в честь Августы Ады дано название одному из современных языком программирования: АДА
* Определение деления с остатком в этом случае содержится в доказательстве, которое нужно выполнить в упражнении 1.15.
*) Конечно, «оператор» может состоять из нескольких операторов – блока, заключенного в фигурные скобки
* См.: Емельянов А.А. Власова Е.А., Дума Р.В. Имитационное моделирование экономических процессов. – М.: Финансы и статистика, 2003. – 368 с.
* Эта особенность есть только в Unix