The mechanics of storage allocation
The exact manner in which static storage is allocated and deallocated depends on the target platform. However, allocating storage for an object with static storage duration typically costs nothing at run time because the compiler, linker, and loader together determine the size and address of the object before the program starts running. From the running program's perspective, an object with static storage duration is always there.
Typical C and C++ programs allocate automatic storage on a run-time stack, often the same stack that they use for storing function-call return addresses. Allocating storage for a local object isn't free, but it's usually dirt cheap--just one machine instruction. For example, in:
int foo(int v)
{
int m;
...
return m;
}
function foo has a single local object, m. The compiler determines m's size from its type, typically 2 or 4 bytes. When it compiles foo, the compiler simply generates an instruction such as:
sub sp, 4
as one of the first instructions in the function body to carve room for an int on the stack. (This example assumes that an int object occupies 4 bytes and that the stack grows downward from higher addresses to lower addresses.)
Allocating automatic storage for several local objects costs more stack space, but no more run time, than allocating storage for just one. For example, in:
int foo(int v)
{
int m;
double d;
...
return m + n;
}
the function has two local objects, m and d. In this case, when it compiles the function, the compiler determines the size of m, still 4, and the size of d, say 8. Rather than generate a separate instruction to allocate storage for each object, the compiler simply adds up the sizes and uses the sum in a single instruction, such as:
sub sp, 12
A function may also declare local objects in nested blocks. For example, in:
int foo(int n)
{
char *p;
...
if (p != NULL)
{
int v;
...
}
return n;
}
function foo has a block nested within the if statement. That block declares a local object v. In this case, the lifetime of the storage for v begins upon entry into the nested block and ends upon exiting the block. However, many compilers will generate code for foo to allocate the storage for v along with all the other local objects upon entering the function and deallocate the storage for v upon exiting foo. Thus, a compiler might generate code that extends the actual lifetime of the storage for a local object, but it's very hazardous for programs to try to exploit these longer lifetimes.
Dynamic allocation is typically much slower than automatic allocation. It often involves executing tens of instructions, possibly more than a hundred. Nonetheless, you can use it to manage memory very economically, and so it may be worth the price.
Linkage on the horizon
As I mentioned earlier, not only can a declaration specify type, scope, and storage duration, it can also specify linkage. I thought linkage would be the subject of this column until I started writing and realized that I needed to cover storage duration first. I'll get there yet.
Dan Saks is president of Saks & Associates, a C/C++ training and consulting company. For more information about Dan Saks, visit his website at www.dansaks.com. Dan also welcomes your feedback: e-mail him at dsaks@wittenberg.edu. For more information about Dan click here .
Endnotes:
1. Saks, Dan, "A New Appreciation for Data Types," Embedded Systems Programming, May 2001, p. 59.
Back
2. Saks, Dan, "Cast with caution," Embedded Systems Design, July 2006, p. 15.
Back
3. Saks, Dan, "Scope regions in C and C++," Embedded Systems Design, November 2007, p. 15.
Back