return 0; implies your program exited successfully. If you want to indicate that your program failed to the program that invoked it, you need to return something else. But what?
Different OSes answered this in different ways. In VMS, it's actually 0 that means failure and 1 that means success. In Unix and most other systems, 0 means success and any non-0 value means failure. What can a portability-conscious programmer do?
Use the standard header <stdlib.h>, and get in the habit of saying exit(EXIT_SUCCESS); and exit(EXIT_FAILURE); instead of 'return'ing. This is guaranteed to work no matter where your program is compiled next.
exit(foo); will work from any subroutine with the same semantics as if it were called from main(). This is useful if you want to have error-handling subroutines end the program if something's gone wrong, and only allow main() to kill itself if everything's peachy. Which is common practice anyway.
For more advanced users, the subroutine atexit(); (also part of C99 and also prototyped in stdlib.h) will register subroutines to be called automatically when your program calls exit();. This makes invoking free(); and other cleanup subroutines fairly brainless, assuming they should be invoked whenever the program ends. Calling free(); on memory you haven't allocated is a bad idea.
Of course, using exit(); alone doesn't allow you to write a recursive main(). If you think you need a main() that calls itself, you should probably rethink the design of your program. If you really need a self-calling main(), you'll need to 'return' from main() until you decide to actually end your program, at which point exit(); is still a good choice.