Что произойдет, если я не вызову fclose() в программе на C?

Во-первых, я знаю, что открывать файл с помощью fopen() и не закрывать его ужасно безответственно и дурной тон. Это чистое любопытство, так что, пожалуйста, пошутите :)

Я знаю, что если программа на C открывает кучу файлов и никогда не закрывает ни один из них, в конце концов fopen() начнет давать сбой. Существуют ли какие-либо другие побочные эффекты, которые могут вызвать проблемы вне самого кода? Например, если у меня есть программа, которая открывает один файл, а затем завершает работу, не закрывая его, может ли это вызвать проблему у человека, запускающего программу? Будет ли такая программа что-нибудь сливать (память, дескрипторы файлов)? Могут ли возникнуть проблемы с повторным доступом к этому файлу после завершения работы программы? Что произойдет, если программу запустить много раз подряд?


person electrodruid    schedule 17.11.2011    source источник
comment
Не надо всех отговорок. Нет ничего ужасного в том, чтобы выйти без вызова fclose, по крайней мере, не в большей степени, чем с использованием goto. Все зависит от ситуации.   -  person R.. GitHub STOP HELPING ICE    schedule 18.11.2011


Ответы (4)


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

Как только ваша программа завершится, операционная система очистится за вами. Он закроет все файлы, которые вы оставили открытыми, когда завершит ваш процесс, и выполнит любую другую очистку, которая необходима (например, если файл был помечен как удаляемый при закрытии, он удалит файл тогда; обратите внимание, что такие вещи являются платформой -специфический).

Однако еще одна проблема, с которой следует быть осторожным, — это буферизация данных. Большинство файловых потоков буферизуют данные в памяти перед их записью на диск. Если вы используете потоки FILE* из библиотеки stdio, то есть две возможности:

  1. Ваша программа завершилась нормально либо путем вызова функции exit(3), либо путем возврата из main (который неявно вызывает exit(3)).
  2. Ваша программа завершилась аварийно; это можно сделать, вызвав abort(3) или _Exit(3), умирание от сигнала/исключения и т.д.

Если ваша программа завершилась нормально, среда выполнения C позаботится об очистке всех открытых буферизованных потоков. Итак, если у вас есть буферизованные данные, записанные в FILE*, которые не были сброшены, они будут сброшены при обычном выходе.

И наоборот, если ваша программа аварийно завершится, любые буферизованные данные не будут сброшены. ОС просто говорит: «Боже мой, вы оставили дескриптор файла открытым, я лучше закрою его для вас», когда процесс завершается; он понятия не имеет, что где-то в памяти лежат какие-то случайные данные, которые программа намеревалась записать на диск, но не сделала этого. Так что будьте осторожны с этим.

person Adam Rosenfield    schedule 17.11.2011
comment
Любые предположения о том, как долго будет существовать этот оставшийся буфер, прежде чем ОС восстановит его? - person user10607; 15.12.2014
comment
Я бы предположил, что как можно быстрее, поскольку этот буфер не волшебный - ваша программа выделила для него память, и ОС должна освободить эту память, когда ваша программа выйдет из строя как часть обычной очистки. - person Xupicor; 08.10.2016
comment
[T] операционная система уберет за вами ... Ну, это действительно зависит от операционной системы. Это сделают основные настольные операционные системы, но это не гарантируется. - person Some programmer dude; 17.09.2019

Стандарт C говорит, что вызов exit (или, что то же самое, возврат из main) приводит к тому, что все открытые объекты FILE закрываются как бы fclose. Так что это совершенно нормально, за исключением того, что вы теряете возможность обнаруживать ошибки записи.

EDIT: Нет такой гарантии для аномального завершения (abort, сбоя assert, получения сигнала, поведение которого по умолчанию заключается в аварийном завершении программы — обратите внимание, что не существует не обязательно любые такие сигналы - и другие средства, определенные реализацией). Как уже говорили другие, современные операционные системы будут очищать все видимые извне ресурсы, такие как открытые дескрипторы файлов на уровне ОС, независимо от того; однако в этом случае FILE скорее всего не будут сброшены.

Конечно, были операционные системы, которые не очищали видимые извне ресурсы при аварийном завершении работы; он имеет тенденцию соглашаться с тем, что не применяются жесткие границы привилегий между «ядром» и «пользовательским» кодом и/или между отдельными «процессами» пользовательского пространства просто потому, что если у вас нет этих границ, это может быть невозможно< /em> чтобы сделать это безопасно во всех случаях. (Рассмотрите, например, что произойдет, если вы запишете мусор поверх таблицы открытых файлов в MS-DOS, что вы вполне можете сделать.)

person zwol    schedule 17.11.2011
comment
Не могли бы вы предоставить ссылку, почему это не зависит от ОС? - person Peter; 18.11.2011
comment
@Peter: C99, §7.20.4.3, ¶ 4: Далее все открытые потоки с незаписанными буферизованными данными очищаются, все открытые потоки закрываются и все файлы, созданные функцией tmpfile, удаляются. Обратите внимание, что это относится только к обычному завершению (exit, return из main), что происходит с _Exit и другими. определяется реализацией. - person Matteo Italia; 18.11.2011
comment
@MatteoItalia, спасибо, но также верно и то, что если процесс неожиданно завершается по какой-либо другой причине, то большинство современных ОС все равно очистят дескрипторы и память. - person Peter; 18.11.2011
comment
@Peter: конечно, но, поскольку вам нужна была ссылка на этот факт, не зависящая от ОС, я предоставил только гарантии, данные стандартом. :) - person Matteo Italia; 18.11.2011

Предполагая, что вы выходите под контролем, используя системный вызов exit() или возвращаясь из main() , то после сброса открытые файловые потоки закрываются. Стандарт C (и POSIX) требует этого.

Если вы выходите из-под контроля (дамп ядра, SIGKILL) и т. д., или если вы используете _exit() или _Exit(), то открытые файловые потоки не сбрасываются (но файловые дескрипторы в конечном итоге закрываются, предполагая POSIX-подобную систему с файловыми дескрипторами — стандарт C не требует наличия файловых дескрипторов). Обратите внимание, что _Exit() требуется стандартом C99, а _exit() - POSIX (но они ведут себя одинаково в системах POSIX). Обратите внимание, что файловые дескрипторы отделены от файловых потоков. См. обсуждение «Последствия завершения программы» на странице POSIX для _exit(), чтобы узнать, что происходит, когда программа завершается в Unix.

person Jonathan Leffler    schedule 17.11.2011
comment
но файловые дескрипторы закрываются - вы уверены? Стандарт говорит, что то, сбрасываются ли открытые потоки с незаписанными буферизованными данными, закрываются ли открытые потоки или удаляются ли временные файлы, определяется реализацией. (C99 §7.20.4.4 ¶2) - person Matteo Italia; 18.11.2011
comment
Потоки не закрыты; файловые дескрипторы есть. Есть (большая) разница. Все файловые дескрипторы закрываются при завершении программы — см. «Последствия завершения программы» на странице _Exit(). Я предполагаю, что система обеспечивает семантику, подобную POSIX; в чистом стандартном C файловых дескрипторов нет. Поведение для систем, отличных от POSIX, может немного отличаться. - person Jonathan Leffler; 18.11.2011
comment
Вот что я говорю: что касается стандарта C, то, что происходит на _Exit(), определяется реализацией (и одно из этих возможных поведений - это то, что предписано стандартом POSIX). - person Matteo Italia; 18.11.2011

Когда процесс умирает, большинство современных операционных систем (особенно ядро) освобождают все ваши дескрипторы и выделенную память.

person Peter    schedule 17.11.2011
comment
По крайней мере, типичная современная операционная система сделает это. Я так понимаю, что раньше в Windows 3.1 и других системах происходила утечка дескрипторов, если приложение не закрывалось корректно. И в наши дни могут быть некоторые другие очень минимальные ОС (возможно, встроенные системы), которые не справляются с этим за вас. - person Ken Smith; 18.11.2011
comment
@KenSmith: это применимо к дескрипторам файлов для конкретной ОС, жестко, потому что стандарт (как сказал @Zack) гарантирует, что при завершении все CRT FILE * закрываются в случае обычного выхода. - person Matteo Italia; 18.11.2011
comment
Хорошая точка зрения; хотя, как вы сказали выше, CRT не гарантирует, что дескрипторы будут закрыты, если приложение завершается ненормально. Как правило, это делает ОС — и, насколько я понимаю, существуют очень старые или очень специализированные операционные системы, которые не дают такой гарантии. - person Ken Smith; 18.11.2011