Есть вопрос об использовании exit
в C ++. В ответе обсуждается, что это не очень хорошая идея в основном из-за RAII, например, если exit
вызывается где-то в коде, деструкторы объектов не будут вызываться, следовательно, если, например, деструктор был предназначен для записи данных в файл, этого не произойдет , потому что деструктор не был вызван.
Мне было интересно, как эта ситуация в C. Применимы ли аналогичные проблемы и в C? Я подумал, поскольку в C мы не используем конструкторы / деструкторы, в C ситуация может быть другой. Так можно ли использовать exit
в C?
Я видел такие функции, как ниже, которые я считаю подходящими для использования в некоторых случаях, но мне было интересно, есть ли у нас аналогичные проблемы в C с использованием exit
, как описано выше, с C ++? (что сделало бы использование таких функций, как ниже, не очень хорошей идеей.).
void die(const char *message)
{
if(errno) {
perror(message);
} else {
printf("ERROR: %s\n", message);
}
exit(1);
}
SIGTERM
сигнала в POSIX и Linux .... Ожидается, что хорошо настроенные серверы справятся с этим хорошо. И вам следует избегать использованияSIGKILL
(то есть попробоватьkill -TERM
тогдаkill -QUIT
и только потомkill -KILL
в качестве системного администратора)exit()
функции неуместна (и удивляет большое количество голосов - это плохой вопрос). Почему мне не следует использовать функцию exit () в C? был бы хорошим кандидатом, если бы у него были ответы калибра этого вопроса. Это не так; обратное закрытие этого как дубликат уместно - и я сделал это.Ответы:
Вместо
abort()
этогоexit()
функция в C считается "изящным" выходом.Из C11 (N1570) 7.22.4.4/p2 Функция выхода (выделено мной):
Стандарт также говорит в 7.22.4.4/p4, что:
Также стоит посмотреть 7.21.3 / p5 Files :
Однако, как упоминается в комментариях ниже, вы не можете предполагать, что он будет охватывать все остальные ресурсы , поэтому вам может потребоваться прибегнуть
atexit()
и определить обратные вызовы для их выпуска индивидуально. На самом деле это именно то, чтоatexit()
нужно делать, как сказано в 7.22.4.2/p2 Функция atexit :Примечательно, что стандарт C не говорит точно, что должно происходить с объектами с выделенной продолжительностью хранения (т. Е.
malloc()
), Поэтому вам нужно знать, как это делается в конкретной реализации. Для современной ОС, ориентированной на хост, вполне вероятно, что система позаботится об этом, но все же вы можете решить это самостоятельно, чтобы отключить отладчики памяти, такие как Valgrind .источник
stdio
, т.е. эквивалентно aclose()
на FD. Я не знаю, в каком смысле @BlueMoon считает это неправильным, но это не менее неверно, чем обращение кclose()
, и, если требуется дальнейшее прояснение, это именно тоatexit()
, для чего.atexit()
можно использовать. Используяexit()
себя не более жестоким , чем делатьreturn
изmain()
.atexit
противном случае это нормально. @BlueMoon: я не думаю, что использование такой функцииdie
означает, что нельзя программировать, в некоторых случаях ее можно использовать. В противном случае вы также можете справиться с этим, используя только возвращаемые значения и т. Д.Да, можно использовать
exit
в C.Чтобы обеспечить все буферы и корректное завершение работы, рекомендуется использовать эту функцию
atexit
, подробнее об этом здесь.Пример кода будет таким:
void cleanup(void){ /* example of closing file pointer and free up memory */ if (fp) fclose(fp); if (ptr) free(ptr); } int main(int argc, char **argv){ /* ... */ atexit(cleanup); /* ... */ return 0; }
Теперь при каждом
exit
вызове функцияcleanup
будет выполняться, что может обеспечить корректное завершение работы, очистку буферов, памяти и т. Д.источник
У вас нет конструкторов и деструкторов, но у вас могут быть ресурсы (например, файлы, потоки, сокеты), и важно их правильно закрыть. Буфер не может быть записан синхронно, поэтому выход из программы без правильного закрытия ресурса может привести к повреждению.
источник
exit()
(а не_exit()
),atexit
процедуры вызываются, иstdio
буферизация сбрасывается на диск.exit()
именно там, чтобы позволить упорядоченный выход из программы.atexit
могут помочь, хотя они не всегда подходят.exit
если хотите, но это глобальный скачок в управлении, который сопровождается всеми недостатками неструктурированного управления, которые мы обсуждали. В качестве способа выхода из состояния отказа это может быть подходящим (и, похоже, это то, что хочет OP), хотя для нормального потока управления я, вероятно, предпочел бы разработать основной цикл с правильным условием выхода, чтобы вы действительно закончили возвращаясь из main.Использование
exit()
нормальноДва основных аспекта разработки кода, которые еще не упоминались, - это «многопоточность» и «библиотеки».
В однопоточной программе в коде, который вы пишете для реализации этой программы, можно использовать
exit()
. Мои программы используют его регулярно, когда что-то пошло не так, и код не собирается восстанавливаться.Но…
Однако звонок
exit()
- это одностороннее действие, которое нельзя отменить. Вот почему и «потоки», и «библиотеки» требуют тщательного обдумывания.Потоковые программы
Если программа многопоточная, то использование
exit()
- это драматическое действие, которое завершает все потоки. Вероятно, будет неуместно выходить из всей программы. Может быть целесообразно выйти из потока, сообщив об ошибке. Если вы знакомы с дизайном программы, то, возможно, такой односторонний выход допустим, но в целом он неприемлем.Код библиотеки
И этот пункт о «осведомленности о структуре программы» применим и к коду в библиотеках. Вызывать библиотечную функцию общего назначения бывает очень редко
exit()
. Вы были бы справедливо расстроены, если бы одна из стандартных функций библиотеки C не вернулась только из-за ошибки. (Очевидно, что функции , какexit()
,_Exit()
,quick_exit()
,abort()
предназначены не для возвращения, то по - другому.) Функции в библиотеке C , следовательно , либо «не может не» или вернуть индикацию ошибки так или иначе. Если вы пишете код для использования в библиотеке общего назначения, вам необходимо тщательно продумать стратегию обработки ошибок для вашего кода. Он должен соответствовать стратегиям обработки ошибок программ, с которыми он предназначен для использования, или обработка ошибок может быть настраиваемой.У меня есть ряд библиотечных функций (в пакете с заголовком
"stderr.h"
, имя, которое ступает по тонкому льду), которые предназначены для выхода, поскольку они используются для сообщения об ошибках. Эти функции выходят по назначению. В том же пакете есть связанная серия функций, которые сообщают об ошибках и не завершаются. Существующие функции, конечно же, реализованы в терминах неоткрывающихся функций, но это внутренняя деталь реализации.У меня есть много других библиотечных функций, и многие из них полагаются на
"stderr.h"
код для сообщения об ошибках. Это дизайнерское решение, которое я принял, и с ним я согласен. Но когда об ошибках сообщают функции, которые выходят, это ограничивает общую полезность кода библиотеки. Если код вызывает функции сообщения об ошибках, которые не завершаются, тогда основные пути кода в функции должны иметь дело с возвратами ошибок разумно - обнаруживать их и передавать сообщение об ошибке в вызывающий код.Код моего пакета отчетов об ошибках доступен в моем репозитории SOQ (Stack Overflow Questions) на GitHub в виде файлов
stderr.c
иstderr.h
в подкаталоге src / libsoq .источник
abort()
когда необходимо выделить память либо,malloc()
либоrealloc()
, представьте себе, что у вас есть приложение, которое связано со 100 библиотеками, и вам интересно, какая из них и как вызвала сбой вашего приложения. Более того, я не нашел упоминанияabort()
в их документации (но не поймите меня неправильно. Это отличная библиотека для своих целей).stderr
обычно буферизируются по строке. Если вывод заканчивается новой строкой, он все равно будет сброшен системой.Одна из причин, по которой следует избегать
exit
использования других функций,main()
- это возможность того, что ваш код может быть вырван из контекста. Помните, что exit - это тип нелокального потока управления . Как неуловимые исключения.Например, вы можете написать некоторые функции управления хранилищем, которые завершаются при критической ошибке диска. Затем кто-то решает переместить их в библиотеку. Выход из библиотеки - это то, что заставит вызывающую программу выйти в бессознательном состоянии, к которому она может быть не готова.
Или вы можете запустить его во встроенной системе. Там некуда выйти , чтобы , все это бежит в
while(1)
петлеmain()
. Это может даже не быть определено в стандартной библиотеке.источник
В зависимости от того, что вы делаете, выход может быть наиболее логичным выходом из программы на C. Я знаю, что это очень полезно для проверки правильности работы цепочек обратных вызовов. Возьмите этот пример обратного вызова, который я использовал недавно:
unsigned char cbShowDataThenExit( unsigned char *data, unsigned short dataSz,unsigned char status) { printf("cbShowDataThenExit with status %X (dataSz %d)\n", status, dataSz); printf("status:%d\n",status); printArray(data,dataSz); cleanUp(); exit(0); }
В основном цикле я настраиваю все для этой системы, а затем жду цикл while (1). Вместо этого можно сделать глобальный флаг для выхода из цикла while, но это просто и делает то, что нужно. Если вы имеете дело с любыми открытыми буферами, такими как файлы и устройства, вы должны очистить их перед закрытием для согласованности.
источник
В большом проекте ужасно, когда может выйти любой код, кроме coredump. Трассировка очень важна для поддержки онлайн-сервера.
источник