Before understanding dynamic memory allocation with malloc, you first need to understand about two very important concepts in C: variable scope and variable lifetime.
The scope of a variable consists of the parts of the code that can "see" that variable. The lifetime of a variable is the part of the code during which the variable is "usable".
Consider the following code:
void g() { int a; /* Point 1 */ } void f() { int b; /* Point 2 */ g(); /* Point 3 */ { int c; /* Point 4 */ } /* Point 5 */ }
I've annotated various points in the code with numbers.
There are three variables in this code, a, b and c.
Let's start with a first. Its scope consists of the body of the g function, from the point of the variable's declaration onwards. Point 1, in other words. None of the other points in the code can "see" the variablea, so the variable is not "in scope" at those points. What about its lifetime? Once g exits, the variable a is no longer usable. That means a's lifetime consists of Point 1 only as well.
OK, what about b? This is declared at the top of function f. Its scope — the points where the code can "see" the variable — consists of Points 2, 3, 4 and 5. You'll note that between Points 2 and 3 the functiong is called, which might lead you to suspect that b is in scope at Point 1 as well. No! At that point b isnot in scope, though it is still alive at that point. What this means is that while the computer is executing function g it can't "see" variable b, but it's not going to destroy the contents of b. When g exits, the variable b is "visible" again.
Finally, consider the variable c. You'll notice that this is declared in a kind of "sub"-block of the functionf. What does this do? It means that c's scope and lifetime consists of Point 4 only. Once that sub-block exits, c is no longer visible, nor is it alive.
In summary:
Variable Scope Lifetime a 1 1 b 2,3,4,5 2,3,1,4,5 c 4 4
You'll notice that scope and lifetime are intricately entwined with the syntax of C — in particular, a variable is "in scope" if you're inside some block or sub-block where the variable was defined, and a variable is "alive" until the block in which it was declared is exited.
OK, so where does malloc and friends fit into all this? The idea of malloc is that one can keep track of a "block of memory" without regard to these normal scoping and visibility restrictions. The variables used toaccess that block memory still have "scope" and "visibility", but the block of memory itself does not.
When you allocate some memory with malloc, C returns a pointer to that memory. It's up to you to store that pointer in a variable somewhere. Furthermore, it's up to you to keep track of the value of that pointer (if not the specific variable) for as long as you want the allocated memory to remain accessible — if you lose the value of the pointer, you've lost the only thing you had to access the memory.
So consider this code:
char * get_memory() { char *a = malloc(42); /* Point 1 */ return a; } void f() { char *b; /* Point 2 */ b = get_memory(); /* Point 3 */ free(b); /* Point 4 */ }
Start with the g function. First, a block of memory 42 bytes in length is allocated. The pointer to this memory is assigned to the variable a. What is a's scope and lifetime? Both consist of just Point 1. This means a can only be used to access that memory block within that one function. The memory block itself exists regardless of the scope and lifetime of variables used to access it — that's why we can safely return the pointer's value from g, and assign it to a different variable in f.
So now let's look at f. The scope of the variable b consists of Points 2, 3 and 4, and its lifetime consists of these points plus Point 1. It's actual value, however, is undefined until something has been assigned to it. In this code snippet, that's occurring between Points 2 and 3.
So at Point 3, b is a variable containing a pointer to that dynamically allocated memory block. The memory block is accessible via that pointer.
After Point 3, we pass the pointer's value into the free function, which tells C to deallocate the memory block. At Point 4, b still points to where the memory block used to be, but, of course, the memory block is no longer there. Any attempt to use it via b would be an error.
You can see here that the memory block allocated by malloc hasn't followed the normal scoping and visibility rules that variables follow. The memory remained "alive" after the g function exited, and it became "dead" before the f function exited. It is "accessible" at any point where there is a "visible" variable pointing to it, whether or not that was the variable to which the memory pointer was originally assigned.
The whole point of malloc is that it gets around the normal rules of variable scoping and visibility. Though I suppose "gets around" is the wrong way to word it. Just about any non-trivial program is going to require the use of memory in ways that doesn't simply follow the normal scoping and visibility rules imposed by the C syntax — pretty much any time you want to allocate memory "here" and deallocate it "over there".
No comments:
Post a Comment