What is the defining characteristic of a local variable?

What is the defining characteristic of a local variable?

Rate This
  • Comments 11

If you ask a dozen C# developers what a "local variable" is, you might get a dozen different answers. A common answer is of course that a local is "a storage location on the stack". But that is describing a local in terms of its implementation details; there is nothing in the C# language that requires that locals be stored on a data structure called "the stack", or that there be one stack per thread. (And of course, locals are often stored in registers, and registers are not the stack.)

A less implementation-detail-laden answer might be that a local variable is a variable whose storage location is "allocated from the temporary store". That is, a local variable is a variable whose lifetime is known to be short; the local's lifetime ends when control leaves the code associated with the local's declaration space.

That too, however, is a lie. The C# specification is surprisingly vague about the lifetime of an "ordinary" local variable, noting that its lifetime is only kinda-sorta that length. The jitter's optimizer is permitted broad latitude in its determination of local lifetime; a local can be cleaned up early or late. The specification also notes that the lifetimes of some local variables are necessarily extended beyond the point where control leaves the method body containing the local declaration. Locals declared in an iterator block, for instance, live on even after control has left the iterator block; they might die only when the iterator is itself collected. Locals that are closed-over outer variables of a lambda are the same way; they live at least as long as the delegate that closes over them. And in the upcoming version of C#, locals declared in async blocks will also have extended lifetimes; when the async method returns to its caller upon encountering an "await", the locals live on and are still there when the method resumes. (And since it might not resume on the same thread, in some bizarre situations, the locals had better not be stored on the stack!)

So if locals are not "variables on the stack" and locals are not "short lifetime variables" then what are locals?

The answer is of course staring you in the face. The defining characteristic of a local is that it can only be accessed by name in the block which declares it; it is local to a block. What makes a local truly unique is that it can only be a private implementation detail of a method body. The name of that local is never of any use to code lexically outside of the method body.

  • That was my thinking too, although you've phrased it much better.

  • It's a shame that this needs to be clarified, but it's a reflection of how most people think, not just programmers: they define things by their side effects rather than by what they are.

  • I tried to define a local variable for myself before reading this article. My definition was reasonably close to yours. This is probably partially due to having read your post on the stack as an implementation detail previously. Having a clear understanding of these things can certainly help avoid bugs due to misconceptions.

  • I would have said "A variable whose reference (or value) is only guaranteed to be valid until the execution of the program leaves the block where it was declared."  

    I may be wrong, but basing the definition on where the name of the variable unambiguously refers to the value seems to wrinkle the conceptual silk of something like an "out" parameter.  I think of a variable passed to a method as an "out" parameter as a local variable in the block where it was declared.   However, I also think of the "out" parameter inside the method to which it was passed as being the same variable declared in the calling block.  Whether or not that's true in terms of implementation, it seems like the design of the language was to encourage the programmer to think of them as being the same variable.

  • >> The defining characteristic of a local is that it can only be accessed by name in the block which declares it

    This merits a question about ref variables ... since a ref/out variable is an alias for a named local variable why is that not considered "referenced by name"? While it's true that the ref/out parameter to a method may have a different name from the variable that is passed to the method, it still seems like an exception to the general characteristic that the variable can only be referenced in the block which declares it; no?

  • @Aaron: Conceptually, "ref" and "out" parameters are something like "aliases for previously defined storage locations", in other words different variables with the same storage location, in a similar manner to "foo.next.prev.bar" being an alias for "foo.bar" despite having a different expression, though with the special attribute of being the only situation in C# where a simple identifier expression is not the "canonical" reference (assuming you consider "this." to be simply syntactic sugar). Of course, implementation-wise it's really just (very well restricted) sugar over pointers....

    @Leo: It's not always a local:

    struct Vector { public float X, Y, Z; }

    class MeshReader

    {

       void Read(out float value) { ... }

       void Read(out Vector value) { Read(out value.X); ... }

    }

    But otherwise that's what the "by name" qualification at the end is about (perhaps Eric edited that in?) - there are plenty of ways to reference that variable (in the sense of reading or writing to it's storage) without using it's name, it's just that ref/out is the only *safe* way to do it.

  • Method parameters cannot be considered the same as local variables using Eric's definition "What makes a local truly unique is that it can only be a private implementation detail of a method body". As C# now allows the parameter's name to be known by the calling method (for named arguments) this would be untrue.

  • I have a hair-splitting exception to the rule. If a function declares an embedded function, perhaps while defining an event handler, the embedded function can "see" the local variables of the outer function.

    (I use this method to define the ItemDataBound handler of an ASP repeater, as I can be sure it'll only be called during teh DataBind function call.)

    In my coding-C-for-too-long head, I see those as separate functions. You might reasonably see such functions as still being within the outer block so is not at all an exception to the rule. Interesting (IMO) nonetheless.

  • To me this definition does not look complete. I'm missing the fact that with each "invocation" a private instance of a local variable is created, in contrast of being a singleton. Concurrent "invocations" (for instance by means of threads or recursion) have their own private instances which do not interfere. "Invocation" is a little bit fuzzy as there are iterator blocks and other funny constructs.

  • @Steffen:  I like Eric's definition because it specifically does not conflate these two concepts.  It just so happens that in C# a local variable nearly always has automatic storage(*), but that's not a requirement in general.  For example, both C and C++ support locat variables with static storage.

    (*) Think about ref and out parameters

  • I am with Bill on this. What about lambdas and anonymous functions closing over local variables? In this case, if you are really creative you can reflect over them through generated class.

Page 1 of 1 (11 items)