Generally you'd localize such a sub-defun if it's only going to be used inside the containing defun. The localization helps by avoiding any name clashes. A very good example if the *error* defun, because there's a built-in *error* defun which simply prints the error - but if your main defun requires some cleanup code you want to happen even after an error, then this is the easiest way to go about it.
What happens is a new *error* defun is placed on top of the stack for the global *error* defun (instead of simply overwriting it). Then while the main defun is still running the top-most *error* defun is in effect. After the main defun completes or closes due to an error, the new localized version of the *error* defun is removed from the stack, making the previous one the effective *error*. There are some problems though, e.g. consider the following:
(defun c:Test1
(/ *error* var1
) (if var1
(setvar "USERI1" var1
)) ;Reset the sysvar to its old value (setq var1
(getvar "USERI1")) ;Save the current value of the sysvar (setvar "USERI1" 0) ;Set the sysvar to something else ;; Continue with some code which might cause an error - e.g. the user pressing Esc
(setvar "USERI1" var1
) ;Reset the sysvar to its old value
(defun c:Test2
(/ *error* var1
) (if var1
(setvar "USERI2" var1
)) ;Reset the sysvar to its old value (setq var1
(getvar "USERI2")) ;Save the current value of the sysvar (setvar "USERI2" 0) ;Set the sysvar to something else ;; Continue with some code which might cause an error - e.g. the user pressing Esc
(c:Test1) ;Call the Test1 defun also
(setvar "USERI2" var1
) ;Reset the sysvar to its old value
Notice since Test2 calls Test1 as well, there's now 3 versions of *error* on the stack. Say an error happens inside Test1, then the error for Test2 is still one place down in the stack and thus won't run when needed and thus USERI2 won't get reset.
Another point about this is variables also have such a stack when localized. E.g. the var1's stack contains 2 versions after Test2 called Test1. So it is also showing only the original value from USERI1 and thus you can thus not get to the original value for USERI2 at this stage.
This is one of the major issues with AutoLisp's error handling: nested errors don't work properly.
As for other defuns, if they aren't declared as localized then they will become global. As if the defun was created outside of the main defun. In which case I'd advise to rather just create it outside in the first place - all else being equal. Sometimes however you're using a feature of Lisp (something many other languages don't have): Runtime self-modification, e.g. say you want a command which acts a certain way in one case but a different way in another, you could have a defun simply redefine that command when called and thew new one will be run - in this case you define a defun inside another without localizing it - because you want to redefine a global only when needed.
So you can see there's good and bad points to the way AutoLisp works. This idea is known as Dynamic Scope, which means any symbol (function / variable) defined as local is in effect while that defun is running. Most other languages (including most other Lisps) have a Lexical Scope (either as well or only Lexical) - which means that localized variables are only applicable inside that defun, even if that defun calls another the 2nd cannot see the vars of the 1st.
Lexical, also known as Static scope, works more like most people expect localization to work. Lexical also usually helps with the problem as per the var1 variable in the above, but it has a cost. If Lexical instead of Dynamic then this wouldn't work:
In AutoLisp that would increment n 10 times and the result would be 10. But if it was Lexical, then it would error, since there's no variable n inside of the RunOnce defun.