Automatic Storage, Static Storage, and Dynamic Storage

C++ has three ways of managing memory for data, depending on the method used to allocate memory: automatic storage, static storage, and dynamic storage, sometimes called the free store or heap. Data objects allocated in these three ways differ from each other in how long they remain in existence. We’ll take a quick look at each type. (C++11 adds a fourth form called thread storage that we’ll discuss briefly in Chapter 9.)

Automatic Storage

Ordinary variables defined inside a function use automatic storage and are called automatic variables. These terms mean that the variables come into existence automatically when the function containing them is invoked, and they expire when the function terminates. For example, the temp array in Listing 4.22 exists only while the getname() function is active. When program control returns to main(), the memory used for temp is freed automatically. If getname() returned the address of temp, the name pointer in main() would be left pointing to a memory location that would soon be reused. That’s one reason you have to use new in getname(). Actually, automatic values are local to the block that contains them. A block is a section of code enclosed between braces. So far, all our blocks have been entire functions. But as you’ll see in the next chapter, you can have blocks within a function. If you define a variable inside one of those blocks, it exists only while the program is executing statements inside the block.

Automatic variables typically are stored on a stack. This means that when program execution enters a block of code, its variables are added consecutively to the stack in memory and then are freed in reverse order when execution leaves the block. (This is called a last-in, first-out, or LIFO, process.) So the stack grows and shrinks as execution proceeds.

Static Storage

Static storage is storage that exists throughout the execution of an entire program. There are two ways to make a variable static. One is to define it externally, outside a function. The other is to use the keyword static when declaring a variable:

static double fee = 56.50;

Under K&R C, you can initialize only static arrays and structures, whereas C++ Release 2.0 (and later) and ANSI C allow you to initialize automatic arrays and structures, too. However, as you may have discovered, some C++ implementations do not yet implement initialization for automatic arrays and structures.

Chapter 9 discusses static storage in more detail. The main point you should note now about automatic and static storage is that these methods rigidly define the lifetime of a variable. Either the variable exists for the entire duration of a program (a static variable) or it exists only while a particular function is being executed (an automatic variable).

Dynamic Storage

The new and delete operators provide a more flexible approach than automatic and static variables. They manage a pool of memory, which C++ refers to as the free store or heap. This pool is separate from the memory used for static and automatic variables. As Listing 4.22 shows, new and delete enable you to allocate memory in one function and free it in another. Thus, the lifetime of the data is not tied arbitrarily to the life of the program or the life of a function. Using new and delete together gives you much more control over how a program uses memory than does using ordinary variables. However, memory management becomes more complex. In a stack, the automatic addition and removal mechanism results in the part of memory in use always being contiguous. But the interplay between new and delete can leave holes in the free store, making keeping track of where to allocate new memory requests more difficult.


Note

Pointers are among the most powerful of C++ tools. They are also the most dangerous because they permit computer-unfriendly actions, such as using an uninitialized pointer to access memory or attempting to free the same memory block twice. Furthermore, until you get used to pointer notation and pointer concepts through practice, pointers can be confusing. Because pointers are an important part of C++ programming, they weave in and out of future discussions in this book. This book discusses pointers several more times. The hope is that each exposure will make you more comfortable with them.


Combinations of Types

This chapter has introduced arrays, structures, and pointers. These can be combined in various ways, so let’s review some of the possibilities, starting with a structure:

struct antarctica_years_end
{
    int year;
 /* some really interesting data, etc. */
};

We can create variables of this type:

antarctica_years_end s01, s02, s03;  // s01, s02, s03 are structures

We can then access members using the membership operator:

s01.year = 1998;

We can create a pointer to such a structure:

antarctica_years_end * pa = &s02;

Provided the pointer has been set to a valid address, we then can use the indirect membership operator to access members:

pa->year = 1999;

We can create arrays of structures:

antarctica_years_end trio[3]; // array of 3 structures

We then can use the membership operator to access members of an element:

trio[0].year = 2003;  // trio[0] is a structure

Here, trio is an array, but trio[0] is a structure, and trio[0].year is a member of that structure. Because an array name is a pointer, we also can use the indirect membership operator:

(trio+1)->year = 2004; // same as trio[1].year = 2004;

We can create an array of pointers:

const antarctica_years_end * arp[3] = {&s01, &s02, &s03};

This is starting to look a bit complicated. How can we access data with this array? Well, if arp is an array of pointers, then arp[1] must be a pointer, and we can use the indirect membership operator with it to access a member:

std::cout << arp[1]->year << std::endl;

We can create a pointer to such an array:

const antarctica_years_end ** ppa = arp;

Here, arp is the name of an array; hence, it is the address of its first element. But its first element is a pointer, so ppa has to be a pointer to a pointer to const antarctica_years_end, hence the **. There are several ways you could mess up this declaration. For example, you could omit the const, forget an * or two, transpose letters, or otherwise mangle the structure type. Here is an instance for which the C++11 version of auto is convenient. The compiler is perfectly aware of what type arp is, so it can deduce the correct type for you:

auto ppb = arp; // C++11 automatic type deduction

In the past, the compiler used its knowledge of the correct type to complain about errors you may have made in the declaration; now it can let its knowledge work for you.

How can you use ppa to access data? Because ppa is a pointer to a pointer to a structure, *ppa is a pointer to a structure, so you can use it with the indirect membership operator:

std::cout << (*ppa)->year << std::endl;
std::cout << (*(ppb+1))->year << std::endl;

Because ppa points to the first member of arp, *ppa is the first member, which is &s01. So (*ppa)->year is the year member of s01. In the second statement, ppb+1 points to the next element, arp[1], which is &s02. The parentheses are needed to get the correct associations. For example, *ppa->year would attempt to apply the * operator to ppa->year, which fails because the year member is not a pointer.

Is all this really true? Listing 4.23 incorporates all the preceding statements into a short program.

Listing 4.23. mixtypes.cpp


// mixtypes.cpp -- some type combinations
#include <iostream>

struct antarctica_years_end
{
    int year;
 /* some really interesting data, etc. */
};

int main()
{
    antarctica_years_end s01, s02, s03;
    s01.year = 1998;
    antarctica_years_end * pa = &s02;
    pa->year = 1999;
    antarctica_years_end trio[3]; // array of 3 structures
    trio[0].year = 2003;
    std::cout << trio->year << std::endl;
    const antarctica_years_end * arp[3] = {&s01, &s02, &s03};
    std::cout << arp[1]->year << std::endl;
    const antarctica_years_end ** ppa = arp;
    auto ppb = arp; // C++11 automatic type deduction
// or else  use const antarctica_years_end ** ppb = arp;
    std::cout << (*ppa)->year << std::endl;
    std::cout << (*(ppb+1))->year << std::endl;
    return 0;
}


Here’s the output:

2003
1999
1998
1999

The program compiles and works as promised.

Array Alternatives

Earlier this chapter mentioned the vector and array template classes as alternatives to the built-in array. Let’s take a brief look now at how they are used and at some of the benefits of using them.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.74.211