Подтвердить что ты не робот

Должны ли мы использовать exit() в C?

Существует question об использовании exit в С++. В ответе говорится, что это не очень хорошая идея в основном из-за RAII, например, если exit вызывается где-то в коде, деструкторы объектов не будут вызываться, следовательно, если, например, деструктор предназначен для записи данных в файл, это не будет, потому что деструктор не был вызван.

Мне было интересно, как эта ситуация на C. Схожие проблемы применимы и в C? Я думал, так как в C мы не используем конструкторы/деструкторы, ситуация может быть различной в C. Так что нормально использовать exit в C?

Я видел такие функции, как, например, ниже, которые я считаю правдоподобными в некоторых случаях, но интересовался, если у нас есть подобные проблемы в C с использованием exit, как описано выше с С++? (что сделало бы использование таких функций, как ниже, не очень хорошая идея.).

void die(const char *message)
{
    if(errno) {
        perror(message);
    } else {
        printf("ERROR: %s\n", message);
    }

    exit(1);
}
4b9b3361

Ответ 1

Вместо abort() функция exit() в C считается "изящным" выходом.

От C11 (N1570) 7.22.4.4/p2 Функция выхода (выделение мое):

Функция exit вызывает нормальное завершение программы.

Стандарт также говорит в 7.22.4.4/p4, что:

Затем все открытые потоки с неписаными буферизованными данными очищаются, все открытые потоки закрыты, а все файлы, созданные функцией tmpfileудаляются.

Также стоит посмотреть файлы 7.21.3/p5:

Если функция main возвращается к исходному вызывающему абоненту или если exitфункция, все открытые файлы закрыты (следовательно, все выходные данные потоки сбрасываются) до завершения программы. Другие пути к завершение программы, например вызов функции abort, не требуется закройте все файлы правильно.

Однако, как упоминалось в комментариях ниже, вы не можете предположить, что он будет охватывать все остальные ресурсы, поэтому вам может потребоваться обратиться к atexit() и определить обратные вызовы для их выпуска по отдельности. На самом деле это именно то, что предполагается atexit(), как сказано в 7.22.4.2/p2. Функция atexit:

Функция atexit регистрирует функцию, на которую указывает func, для без аргументов при нормальном завершении программы.

Примечательно, что в стандарте C точно не указано, что должно происходить с объектами выделенной продолжительности хранения (т.е. malloc()), поэтому вам нужно знать, как это делается для конкретной реализации. Для современных ОС, ориентированных на хост, вполне вероятно, что система позаботится об этом, но все-таки вы можете справиться с этим сами, чтобы отключить отладчики памяти, такие как Valgrind.

Ответ 2

Да, нормально использовать 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 будет выполнена, что может содержать изящное завершение работы, очистку буферов, памяти и т.д.

Ответ 3

У вас нет конструкторов и деструкторов, но у вас могут быть ресурсы (например, файлы, потоки, сокеты), и важно их правильно закрыть. Буфер нельзя было записать синхронно, поэтому выход из программы без правильного закрытия ресурса может привести к повреждению.

Ответ 4

Использование exit() в порядке

Двумя основными аспектами разработки кода, которые еще не были упомянуты, являются "потоковые" и "библиотеки".

В однопоточной программе в коде, который вы пишете для реализации этой программы, использование exit() в порядке. Мои программы используют его регулярно, когда что-то пошло не так, и код не восстановится.

Но...

Однако вызов exit() - одностороннее действие, которое нельзя отменить. Вот почему и "потоковая", и "библиотека" требуют тщательной мысли.

Резьбовые программы

Если программа многопоточная, то использование exit() - это драматическое действие, которое завершает все потоки. Вероятно, будет нецелесообразно покидать всю программу. Может быть целесообразным выйти из потока, сообщая об ошибке. Если вы понимаете дизайн программы, то, возможно, это односторонний выход допустим, но в целом это будет неприемлемо.

Код библиотеки

И это понятие, описывающее конструкцию предложения программы, относится и к коду в библиотеках. Очень редко для библиотечной функции общего назначения вызывается exit(). Вы были бы оправданы, если бы одна из стандартных функций библиотеки C не смогла вернуться только из-за ошибки. (Очевидно, что функции типа exit(), _Exit(), quick_exit(), abort() предназначены не для возврата, а для разных.) Таким образом, функции в библиотеке C либо "не могут потерпеть неудачу", либо как-то вернуть индикацию ошибки, Если вы пишете код, чтобы перейти в библиотеку общего назначения, вам необходимо внимательно рассмотреть стратегию обработки ошибок для вашего кода. Он должен соответствовать стратегиям обработки ошибок программ, с которыми он предназначен для использования, или может обрабатываться обработка ошибок.

У меня есть ряд библиотечных функций (в пакете с заголовком "stderr.h", которое проходит на тонком льду), которые предназначены для выхода, поскольку они используются для сообщения об ошибках. Эти функции выходят по дизайну. В одном пакете есть связанная серия функций, которые сообщают об ошибках и не выходят. Конечно, выходящие функции реализованы с точки зрения не выходящих функций, но это внутренняя деталь реализации.

У меня много других функций библиотеки, и многие из них полагаются на код "stderr.h" для сообщения об ошибках. Это дизайнерское решение, которое я сделал, и это тот, с которым я в порядке. Но когда сообщения об ошибках выводятся с выходами, это ограничивает общую полезность библиотечного кода. Если код вызывает функции отчетов об ошибках, которые не выходят из системы, то основные пути кода в функции должны иметь дело с ошибками, чтобы восстановить их корректно - обнаружить их и передать сообщение об ошибке в вызывающий код.

Ответ 5

Одна из причин избежать exit в функциях, отличных от main(), - это возможность того, что ваш код может быть выведен из контекста. Помните, что выход - это тип не локального потока управления. Подобно неуловимым исключениям.

Например, вы можете написать некоторые функции управления хранением данных, которые выходят на критическую ошибку диска. Затем кто-то решает переместить их в библиотеку. Выход из библиотеки - это то, что приведет к выходу вызывающей программы в несогласованное состояние, для которого она не может быть подготовлена.

Или вы можете запустить его во встроенной системе. Выход некуда, все работает в цикле while(1) в main(). Это может быть даже не определено в стандартной библиотеке.

Ответ 6

В зависимости от того, что вы делаете, выход может быть самым логичным выходом из программы в 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);
}

В основном цикле я настроил все для этой системы, а затем подождал (1) цикл. Можно сделать глобальный флаг для выхода из цикла while вместо этого, но это просто и делает то, что ему нужно сделать. Если вы имеете дело с любыми открытыми буферами, такими как файлы и устройства, вы должны очистить их до закрытия для согласованности.

Ответ 7

Это ужасно в большом проекте, когда любой код может выйти за исключением coredump. Трассировка очень важна для поддержки онлайн-сервера.