Scoping tells us which
i is being referred to when a line of code does so.
However, in your example, there is only one
i, and it is in the global scope. Since there is no local
i declaration (neither as a parameter, nor an automatic variable), uses of
i within the function must be referring to the global
Static scoping tells us which
i, however, (recursive) activation is an orthogonal concept.
If you had a local variable
i in scope, then the binding of uses of
i would go that local variable.
And when recursion is used, the function is activated multiple times, and each activation gets its own
i. Each use of
i refers to the local variable in the current activation of the function no matter the recursive depth.
Activation is an inherent property of languages that provide recursion, and any currently executing function knows its specific activation.
C doesn't support closures, so the conversation ends there for C.
However, even when the static scoping says that a variable binds to an outer enclosing context, if that outer enclosing context is recursively activated, there will be multiple copies of those variables and the appropriate one is chosen by following first the activation context, and then applying static scoping.
To your comments below:
does static scoping only determine which declaration in the code that a name refers to, but not determine anything about execution, while activations are about execution of the code?
(1) Yes. Though note that execution is constrained to follow the static scoping as follows: given a function and a nested function, if the outer function scope has a variable
i from the static scope perspective, there will be some activation that corresponds to that static scope that introduced that
i available when the inner scope runs (also with its own activation), otherwise the inner scope could not have been activated itself. So, the static scope can be mapped to the activation, and vice versa, and we can say that the static scope at least tells us something about the pattern of activations.
2). does dynamic scoping determine the same thing as static scoping does? So is it correct that dynamic scoping doesn't determine anything related to execution of the code, so dynamic scoping and activations are also orthogonal concepts to each other? But the way that dynamic scoping uses for the determination depend on execution of the code?
(2) Dynamic scoping, depending on language, looks up the variables by the current activation stack (from most nested outward) rather than by the static scope. Thus, there are inherent runtime implications, which is to say, it is much harder to optimize since lookup depends on the runtime call chain.
(Dynamic scoping is just plain confusing, if you ask me, though I'm sure some programming problems lend themselves to that nicely.)
In any case, if an (inner) function declares its own local
i is going to be the one, whether statically scoped or dynamically scoped, and regardless of recursion.
The difference comes into play when the
i isn't declared locally, so what gets found as a "non-local" reference to
i is the difference. In a dynamically scoped system, the nearest
i on the activation stack would bind to the use. This means that at runtime the activation stack is searched for an
i. It probably doesn't have to do string matching to find an
i (though that is one possibility), but there has to be a bunch of machinery around declaring local variables for binding re: dynamic scoping (which is not exactly so necessary with static scoping).
(Whereas with static scoping, the trick is in having the activations available, given those, you "know" from compile time where
i is within its outer scope, as you know statically what the exact function of that outer scope is. In statically scoped system, a use of
i would refer to the
i that corresponded to the activation of that outer scope that also activated the currently running inner scope, so the outer scope and the inner scope are in some sense well connected. By contrast, with dynamic scoping, completely different outer activations might hold the
i of interest depending on the call chain.)
A directly recursive function that didn't declare a local
i, would not find an
i in itself or any recursive activation of itself, so would find
i in some outer activation (but it could be any number of different possible functions that has an
If the recursive function participated in mutual recursion where one (or more) of the other functions did declare an
i, that most recently activated one would supply any non-local usage of
i. Whew! I have never programed that way so I otherwise don't have my head wrapped around how that would be useful. (And as a compiler writer, I see significant performance challenges for dynamic scoping, though have never written a compiler for a dynamically scoped language.)