Each variable name, or function name, etc., corresponds to a location in memory where the corresponding information or code is stored. (That's a slight simplification but you get the idea). A scope represents a mapping from names to locations. Think of it as a dictionary or similar data structure in your favorite language.
Nested scopes work as a sort of stack. When the language syntax introduces a new scope, a new mapping is pushed to the stack. When you use a variable name, say i
, the compiler or interpreter looks for it at the top-level mapping, then (if it's not found there) upwards according to the rules of the language. When a scope ends, the relevant mapping is popped off the stack and the previous mapping comes back in effect.
The ugly details of how this works could vary: In the simplest case, a compiler could generate code that refers to memory locations directly. More realistically: a C compiler produces executable objects that include a "symbol table" which maps variable and function names to locations in memory. The linker resolves references between modules, by looking up references from one module in the symbol table of another. And names are not mapped to absolute locations, but to offsets from some reference point. This allows libraries to be "relocatable code", which means they could be loaded to any location in memory during execution and still work properly. Languages that compile to a virtual machine may cut some corners, but the principles are the same.