C17 7.21.3 (4):
Значение указателя на объект FILE не определено после закрытия связанного файла.
Так в вашем fputc
вызов, fp
имеет неопределенное значение. Стандарт определяет поведение для fputc
только тогда, когда fp
указывает на выходной поток, и мы не можем сказать, что это так, поэтому поведение не определено.
Текст немного вводит в заблуждение, потому что на типичная система, значение указателя не меняется при передаче в fclose
; в конце концов, C передает аргументы по значению, поэтому FILE *fp; ...; fclose(fp);
не может изменить значение вашей fp
переменной, даже если бы захотел. Он по-прежнему указывает на тот же адрес, что и всегда. Но данные, расположенные по этому адресу, определенно могут стать неопределенными, и для системы больше не должно иметь смысла интерпретировать их как поток.
Вот что происходит под капотом Linux. Он должен go, не говоря, что это все детали реализации, и вы не должны полагаться на них в какой-либо программе.
Вы можете увидеть здесь что Linux на самом деле fputc
делает. Параметр fp
указывает на объект FILE
, который содержит указатель на буфер и числа, указывающие, сколько места осталось. Если в буфере есть место, туда записывается символ; это не может потерпеть неудачу. Только если буфер заполняется и данные должны быть записаны через ОС, есть возможность для fputc
вернуть ошибку.
Теперь, когда вы fclose
файл, буфер и FILE
объект просто освобождается с помощью free()
или аналогичного механизма. Если за это время никто другой не выделил память, содержимое этих объектов может все еще находиться в памяти и не измениться к моменту вызова fputc
. Эти объекты никоим образом не помечаются как недопустимые до этого, потому что никто никогда не увидит флаг, если они не будут обращаться к освобожденной памяти, и никакая правильная программа никогда не должна этого делать. Поэтому, когда вы вызываете fputc
, содержимое памяти, на которое указывает fp
, по-прежнему выглядит точно так же, как действительный объект FILE
, с не заполненным буфером, поэтому fp
записывает туда символ и возвращает успех - потому что, в конце концов, запись символа в буфер невозможна. Но на самом деле теперь вы записали в освобожденную память, и это потенциально может привести к всевозможным проблемам.
Это похоже на ошибки «использования после освобождения» с mallo c: система не доверяет вам использовать ресурс после того, как вы его выпустили, но не обещает поймать вас, если вы это сделаете.
Другие системы, которые вы тестировали, скорее всего, имеют какой-то «недействительный» флаг в объекте FILE
, который они устанавливают перед его освобождением, и их fputc
, вероятно, проверяет этот флаг. Таким образом, если объект FILE
не был перезаписан другим материалом, значение флага все еще может быть в памяти, и поэтому fputc
не выполняется. Но это все равно неправильно, потому что ему нужно было прочитать освобожденную память, чтобы даже увидеть флаг. Если вы проделаете кучу дополнительных работ между ними, включая выделение большего объема памяти и запись в нее, вы также можете увидеть более непредсказуемое неправильное поведение в этих системах, возможно, включая ложные результаты успеха.