Actually being tidy can be interesting when your program evolves.It forces you to write cleanup function when you create "initialisation" functions. The benefit comes when your program becomes more complex, and you want to restart part of the program. If you have already written working cleanup functions, it is less likely that you suddenly forgot some cleanup when "restarting" part of your program.
Writing cleanup functions "lazily" ie only when you need it is more error-prone. Writing cleanup functions forces you to think about cleanup and eventual cleanup dependency. It permit easier code reuse of a part of your code in another project.
So yes freeing in an atexit is useless, and so is closing file descriptor. However writing and maintaining cleanup function as your code grows can be a constraint that will force you to think about what you are doing