int main(int argc, char **argv) int main(int argc, char **argv)
{ {
/* здесь код */ /* здесь код */
return 0; /* ?? Что возвращает main()? */
} }
Стандарт С 1999 г. указывает, что при выпадении в конце, поведение функции main()
должно быть таким, как если бы она возвращала 0. (Это верно также для С++; однако, стандарт С 1989 г. намеренно оставляет этот случай неопределенным.) Во всех случаях плохо полагаться на это поведение; однажды вы можете программировать для системы со скудной поддержкой С времени исполнения, или для внедренной системы, или где-то еще, где это будет по-другому. (В общем, выпадение в конце любой функции, не являющейся void
— плохая мысль, которая может вести лишь к ошибочному коду.)
Возвращенное из main()
значение автоматически передается обратно системе, от которой родительский процесс может его впоследствии получить. Мы опишем, как это делается, в разделе 9.1.6.1 «Использование функций POSIX: wait()
и waitpid()
».
ЗАМЕЧАНИЕ. На системах GNU/Linux управляемая компилятором команда c99 запускает компилятор с соответствующими опциями, так что возвращаемое значение при выпадении из конца функции равно 0. Простой gcc этого не делает.
9.1.5.3. Функции завершения
Другим способом естественного завершения программы является вызов функций завершения. Стандарт С определяет следующие функции:
#include
void exit(int status);
void _Exit(int status);
int atexit(void (*function)(void));
Эти функции работают следующим образом:
void exit(int status)
Эта функция завершает программу, status
передается системе для использования родителем. Перед завершением программы exit()
вызывает все функции, зарегистрированные с помощью atexit()
, сбрасывает на диск и закрывает все открытые потоки <stdio.h> FILE
* и удаляет все временные файлы, созданные tmpfile()
(см. раздел 12.3.2 «Создание и открытие временных файлов»). Когда процесс завершается, ядро закрывает любые оставшиеся открытыми файлы (которые были открыты посредством open()
, creat()
или через наследование дескрипторов), освобождает его адресное пространство и освобождает любые другие ресурсы, которые он мог использовать. exit()
никогда не возвращается.
void _Exit(int status)
Эта функция в сущности идентична функции POSIX _exit()
; мы на короткое время отложим ее обсуждение,
int atexit(void (*function)(void))
function
является указателем на функцию обратного вызова, которая должна вызываться при завершении программы, exit()
запускает функцию обратного вызова перед закрытием файлов и завершением. Идея в том, что приложение может предоставить одну или более функций очистки, которые должны быть запущены перед окончательным завершением работы. Предоставление функции называется ее nftw()
обсуждались в разделе 8.4.3.2 «Функция обратного вызова nftw()
»; здесь та же идея, хотя atexit()
вызывает каждую зарегистрированную функцию лишь однажды.)
atexit()
возвращает 0 при успехе или -1 при неудаче и соответствующим образом устанавливает errno
.
Следующая программа не делает полезной работы, но демонстрирует, как работает atexit()
:
/* ch09-atexit.c --- демонстрация atexit().
Проверка ошибок для краткости опущена. */
/*
* Функции обратного вызова здесь просто отвечают на вызов.
* В настоящем приложении они делали бы больше. */
void callback1(void) { printf("callback1 called\n"); }
void callback2(void) { printf("callback2 called\n"); }
void callback3(void) { printf("callback3 called\n"); }
/* main --- регистрация функций и завершение */
int main(int argc, char **argv) {
printf("registering callback1\n"); atexit(callback1);