Showing posts with label memory management. Show all posts
Showing posts with label memory management. Show all posts

Wednesday, September 1, 2010

The stack and the heap

The memory a program uses is typically divided into four different areas:
  • The code area, where the compiled program sits in memory.
  • The globals area, where global variables are stored.
  • The heap, where dynamically allocated variables are allocated from.
  • The stack, where parameters and local variables are allocated from.
There isn’t really much to say about the first two areas. The heap and the stack are where most of the interesting stuff takes place, and those are the two that will be the focus of this section.
The heap
The heap (also known as the “free store”) is a large pool of memory used for dynamic allocation. In C++, when you use the new operator to allocate memory, this memory is assigned from the heap.
1int *pValue = new int; // pValue is assigned 4 bytes from the heap
2int *pArray = new int[10]; // pArray is assigned 40 bytes from the heap
Because the precise location of the memory allocated is not known in advance, the memory allocated has to be accessed indirectly — which is why new returns a pointer. You do not have to worry about the mechanics behind the process of how free memory is located and allocated to the user. However, it is worth knowing that sequential memory requests may not result in sequential memory addresses being allocated!
1int *pValue1 = new int;
2int *pValue2 = new int;
3// pValue1 and pValue2 may not have sequential addresses
When a dynamically allocated variable is deleted, the memory is “returned” to the heap and can then be reassigned as future allocation requests are received.
The heap has advantages and disadvantages:
1) Allocated memory stays allocated until it is specifically deallocated (beware memory leaks).
2) Dynamically allocated memory must be accessed through a pointer.
3) Because the heap is a big pool of memory, large arrays, structures, or classes should be allocated here.
The stack
The call stack (usually referred to as “the stack”) has a much more interesting role to play. Before we talk about the call stack, which refers to a particular portion of memory, let’s talk about what a stack is.
Consider a stack of plates in a cafeteria. Because each plate is heavy and they are stacked, you can really only do one of three things:
1) Look at the surface of the top plate
2) Take the top plate off the stack
3) Put a new plate on top of the stack
In computer programming, a stack is a container that holds other variables (much like an array). However, whereas an array lets you access and modify elements in any order you wish, a stack is more limited. The operations that can be performed on a stack are identical to the ones above:
1) Look at the top item on the stack (usually done via a function called top())
2) Take the top item off of the stack (done via a function called pop())
3) Put a new item on top of the stack (done via a function called push())
A stack is a last-in, first-out (LIFO) structure. The last item pushed onto the stack will be the first item popped off. If you put a new plate on top of the stack, anybody who takes a plate from the stack will take the plate you just pushed on first. Last on, first off. As items are pushed onto a stack, the stack grows larger — as items are popped off, the stack grows smaller.
The plate analogy is a pretty good analogy as to how the call stack works, but we can actually make an even better analogy. Consider a bunch of mailboxes, all stacked on top of each other. Each mailbox can only hold one item, and all mailboxes start out empty. Furthermore, each mailbox is nailed to the mailbox below it, so the number of mailboxes can not be changed. If we can’t change the number of mailboxes, how do we get a stack-like behavior?
First, we use a marker (like a post-it note) to keep track of where the bottom-most empty mailbox is. In the beginning, this will be the lowest mailbox. When we push an item onto our mailbox stack, we put it in the mailbox that is marked (which is the first empty mailbox), and move the marker up one mailbox. When we pop an item off the stack, we move the marker down one mailbox and remove the item from that mailbox. Anything below the marker is considered “on the stack”. Anything at the marker or above the marker is not on the stack.
This is almost exactly analogous to how the call stack works. The call stack is a fixed-size chunk of sequential memory addresses. The mailboxes are memory addresses, and the “items” are pieces of data (typically either variables or addreses). The “marker” is a register (a small piece of memory) in the CPU known as the stack pointer. The stack pointer keeps track of where the top of the stack currently is.
The only difference between our hypothetical mailbox stack and the call stack is that when we pop an item off the call stack, we don’t have to erase the memory (the equivalent of emptying the mailbox). We can just leave it to be overwritten by the next item pushed to that piece of memory. Because the stack pointer will be below that memory location, we know that memory location is not on the stack.
So what do we push onto our call stack? Parameters, local variables, and… function calls.
The stack in action
Because parameters and local variables essentially belong to a function, we really only need to consider what happens on the stack when we call a function. Here is the sequence of steps that takes place when a function is called:
  1. The address of the instruction beyond the function call is pushed onto the stack. This is how the CPU remembers where to go after the function returns.
  2. Room is made on the stack for the function’s return type. This is just a placeholder for now.
  3. The CPU jumps to the function’s code.
  4. The current top of the stack is held in a special pointer called the stack frame. Everything added to the stack after this point is considered “local” to the function.
  5. All function arguments are placed on the stack.
  6. The instructions inside of the function begin executing.
  7. Local variables are pushed onto the stack as they are defined.
When the function terminates, the following steps happen:
  1. The function’s return value is copied into the placeholder that was put on the stack for this purpose.
  2. Everything after the stack frame pointer is popped off. This destroys all local variables and arguments.
  3. The return value is popped off the stack and is assigned as the value of the function. If the value of the function isn’t assigned to anything, no assignment takes place, and the value is lost.
  4. The address of the next instruction to execute is popped off the stack, and the CPU resumes execution at that instruction.
Typically, it is not important to know all the details about how the call stack works. However, understanding that functions are effectively pushed on the stack when they are called and popped off when they return gives you the fundamentals needed to understand recursion, as well as some other concepts that are useful when debugging.
Stack overflow
The stack has a limited size, and consequently can only hold a limited amount of information. If the program tries to put too much information on the stack, stack overflow will result. Stack overflow happens when all the memory in the stack has been allocated — in that case, further allocations begin overflowing into other sections of memory.
Stack overflow is generally the result of allocating too many variables on the stack, and/or making too many nested function calls (where function A calls function B calls function C calls function D etc…) Overflowing the stack generally causes the program to crash.
Here is an example program that causes a stack overflow. You can run it on your system and watch it crash:
1int main()
2{
3    int nStack[100000000];
4    return 0;
5}
This program tries to allocate a huge array on the stack. Because the stack is not large enough to handle this array, the array allocation overflows into portions of memory the program is not allowed to use. Consequently, the program crashes.
The stack has advantages and disadvantages:
  • Memory allocated on the stack stays in scope as long as it is on the stack. It is destroyed when it is popped off the stack.
  • All memory allocated on the stack is known at compile time. Consequently, this memory can be accessed directly through a variable.
  • Because the stack is relatively small, it is generally not a good idea to do anything that eats up lots of stack space. This includes allocating large arrays, structures, and classes, as well as heavy recursion.

Wednesday, August 4, 2010

Memory Management Basics

Memory is central to the operation of a modern computer System. Memory consists of a large array of words or bytes each with its own address. The CPU fetches instructions from memory according to the value of the program counter. These instructions may cause additional loading from and storing to specific memory addresses. I have discussed a typical instruction-execution cycle in the previous article. In this article I will discuss on basic hardware issues, the binding of symbolic memory addresses to actual physical addresses and distinguishing between logical and physical addresses.

Basic Hardware

Main memory and the registers built into the processor itself are the only storage that the CPU can access directly. There are machine instructions that take memory addresses as arguments, but none that take disk addresses. Therefore, any instructions in execution, and any data being used by the instructions, must be in one of these direct-access storage devices. If the data are not in memory, they must be moved there before the CPU can operate on them.

Registers that are built into the CPU are generally accessible within one cycle of the CPU clock. Most CPU can decode instructions and perform simple operations on register contents at the rate of one or more operations per clock tick. The same cannot be said of main memory, which is accessed via a transaction on the memory bus. Memory access may take many cycles of the CPU clock to complete, in which case the processor normally needs to stall, since it does not have the data required to complete the instruction that it is executing. The situation is intolerable because of the frequency of memory accesses. The remedy is to add fast memory between the CPU and main memory. A memory buffer used to accommodate a speed differential is called a cache.

Main memory is divided into two parts. One part is to hold the operating system instructions, where as the other part will hold the program which is currently being executed by the user (i.e., in a uni-programming). In a multiprogramming system, the user part is further subdivided, for accommodating multiple processes. The task of subdivision is carried out dynamically by the operating system is known as memory management. Memory management module of an Operating System is concerned with organization of main memory. Memory management is concerned with:
  • keeping track of the status of each memory location i.e. allocated or free.
  • determining allocation policy for memory.
  • reclaiming previously allocated memory.


Memory Management Schemes



There are two memory management schemes:
  • Single User Case-Mono Programming
  • Multiple User Case-Multi Programming
Single User-Mono Programming

It is a simple memory management scheme and associated with simple microcomputers. In this scheme memory is divided up between the OS and a single user process. The OS is protected from user programs (to prevent user program overwriting OS code).

Advantages
  • Simplicity
  • Small OS
Disadvantages
  • Poor utilization of memory. It is wasteful.
  • Poor utilization of processor.
  • User address space may contain information that is never used.
  • Low flexibility. User's job limited to the size of available memory.
Multiple Users-Multiprogramming

It is a memory management scheme in which a user can operate on more than one job at a time. Resources are distributed among jobs. In this scheme there are two problems- relocation and protection. The solution to this problem is to use register-base and limit register. We need to maximize the degree of multiprogramming i.e. the number of processes in memory.

Memory-Management Requirements



Memory management should satisfy the following requirements:
  • Relocation
  • Sharing
  • Protection
  • Logical organization
  • Physical organization.
Relocation

Relocation is a basic requirement of memory management. For simplification purposes let us assume that the process image occupies a contiguous region of main memory. The operating system need to know the location of:
  • Process control information
  • Execution stack
  • Entry point to begin the execution of a program.
    • ‘Processor’ must deal with memory references within the program.
    • ‘Branch instructions’ contain an address to reference the instruction to be executed next.
    • ‘Data reference instructions’ contain the address of the byte or word of data referenced.
The figure below depicts the above concept:



Sharing

Any protection mechanism must have the flexibility to allow several processes to access the same portion of main memory.

E.g., if numbers of processes are executing the same program then it is advantageous to allow each process to access the same copy of the program rather than its own separate copy.

Protection

Once we have two programs in memory at the same time there is a danger that one program can write to the address space of another program. Every process should be protected against unwanted interference by other processes. Satisfaction of the relocation requirement increases the difficulty of satisfying the protection requirement.CPU tend to support absolute addressing which means that code runs differently when loaded in different places.

It is not possible to check the absolute address at compile time. Most of the programming languages allow the dynamic calculation of address at run time.

Logical Organization

Main memory is organized as a linear or one-dimensional address space that consists of sequence of bytes or words. Secondary memory at its physical level is similarly organized. Most of the programs are organized into modules.

Advantages of Modules
  • Modules can be written and compiled independently
  • Protection (such as read-only, execute-only) is given to different modules.
  • Introduce mechanism by which modules can be shared among processes.
Physical Organization

Computer memory is organized into two levels:
  • Main memory - Main memory is a volatile memory and it provides fast access at relatively high cost.
  • Secondary memory - Secondary memory is a non-volatile memory and it is slower and cheaper than main memory.
The organization of the flow of information between main memory and secondary memory is a major system concern. It is impractical for the programmer, because of the following reasons.
  • Main memory available for a program plus its data, may be insufficient. In this case programmer must use the mechanism known as "overlaying". That is keep in memory only those instructions and data that are needed at any given time.
  • In a multiprogramming environment, programmer does not know how much space is available and that space is at where at the time of coding.
There are two main ways to make code relocatable:
  • We can have the compiler/linker mark all the address in the code and rewrite them all when the program is loaded. This adds start up overhead.
  • Use only relative addressing and require the OS to set a base register to the beginning of the code. This is particularly useful in base/limit protected system because the same base register can be used for relocation and protection.
  • Relocation without protection is dangerous. It is possible to create an absolute addressing instruction and use it to access some other processes memory.
  • A solution which solves both the relocation and protection problem is to use two registers called base and limit registers. The base register stores the start address of the position and the limit register holds the length of the portions.
  • Any address that is generated by the program has the base register added to it. In addition all addresses are checked to ensure that they are within the range of partition.
  • An additional benefit of this scheme is that if a program is moved within memory only its base register need to be amended.
Address Binding

Usually, a program resides on a disk as a binary executable file. To be executed, the program must be brought into memory and placed within a process. Depending on the memory management in use, the process may be moved between disk and memory during its execution. The processes on the disk that are waiting to be brought into memory for execution from the input queue.

The normal procedure is to select one of the processes in the input queue and to load that process into memory. As the process is executed, it accesses instructions and data from memory. Eventually, the process terminates, and its memory space is declared available.

Most systems allow a user process to reside in any part of the physical memory. Thus, although the address space of the computer starts at 00000, the first address of the user process need not be 00000. This approach affects the addresses that the user program can use. In most cases, a user program will go through several steps- some of which may be optional- before being executed. Addresses in the source program are generally symbolic (such as count). a compiler will typically bind these symbolic addresses to relocatable addresses(such as "14 bytes from the beginning of this module"). The linkage editor or loader will in turn bind the relocatable addresses to absolute addresses (such as 74014). Each binding is mapping from one address space to another.

Address Binding is a process which fixes a physical address to the logical address of a process' address space. There are various types of binding as below:
  • Compile time binding - If the program location is fixed and it is known at compile time where the process will reside in memory is called compile time binding. If at some later time the starting location changes then it will be necessary to recompile this code. The MS-DOS .COM format programs are bound at compile time.
  • Load time binding - If it is not known at compile time where the process will reside in memory, then the compiler must generate relocatable code. In this case, final binding is delayed until load time. If the starting address changes, we need only reload the user code to incorporate this changed value.
  • Execution time binding - If the process can moved during its execution from one memory segment to another, then binding must be delayed until run time. Special hardware must be available for this scheme to work. Most general-purpose operating systems use this method.

Logical & Physical Address Space



The concept of logical address space that is bound to a separate physical address space is central to proper memory management.

Logical address is the address that is generated by the CPU. It is also referred to as virtual address.

Physical address is the address as seen by the memory management unit that is the one that is loaded into the memory address register of the memory.

For example, P2 is a program, with size 256 KB. But program is loaded in the main memory from 13000 to 13256 KB; this address is called physical address.



Physical address space = Logical address space + relocation register value

13256 = 256 + 13000

In compile-time and load-time address-binding schemes logical and physical addresses are same. Whereas in execution-time the address-binding schemes will differ. Here logical address is virtual-address.

The set of all logical addresses (generated by a program) is referred ta as Logical address space. The set of all Physical addresses corresponding to these logical addresses is referred to as physical address space. The run-time mapping from logical (virtual) to physical addresses is done by the MMU (memory-management unit), which is a hardware device.We can choose from many different methods to accomplish such mapping. This will be discussed in the next article.

Base register is called relocation register. User program never sees the real physical addresses. User program deals with the logical addresses only.

Memory Management In C and C + +

For simple applications, it's enough just to rely on automatic memory management through local variables. But once the data become larger, it is no longer imperative to request memory from the heap and manage.

Content


  1. Function Families
  2. Allocators
  3. Toolbox
  4. Smart Pointer
  5. STL Containers

A function families



In principle, belong to the memory always two parts. First, an area of memory are requested, and second, the memory used at the end of the work be returned. For this reason, memory management functions normally exist in pairs, sharing the work.

When working with C + + objects is yet to fill the requested memory with sensible starting values or the data of the object at the end of the work to clean up again. For this purpose, there are constructors and destructors that are called automatically to suit the use or management must be handled manually.

Over time, various function pairs were developed, which can be requested and memory freed. It is particularly important to the different methods of memory management not to interfere with each other. In the simplest case, it is "just" a memory leak (if the sharing function "just forget" to call the Objektdestruktor or returns too little heap memory), in the worst case, can not be predicted what will make the program (for example, it can happen that the release function tries to interpret something in her memory structure unknown).

For this reason, libraries, heap areas and request the user to pass, always provide a compatible release function, which is suitable to its own working memory - or in the documentation at least set the method of the memory they use.

1.1 malloc () and free ()

This family dates back to C and has been for reasons of compatibility for C + +: Accepted
  • malloc (s)
    allocates a memory area with (at least) s bytes in size and returns a pointer to the beginning of this area. The content of this field is undefined.
  • calloc (n, s)
    enough allocates memory for n objects of size s and initialize it with zeros
  • realloc (p, s)
    the amount of memory changes, the p shows on s bytes and returns the new address of the memory (realloc (NULL, s) "is equivalent to 'malloc (s)" and "realloc (p, 0)" corresponds "free (p)")
  • free (p)
    there by the malloc () etc. requested memory range again free (p must be the NULL pointer, in which case nothing happens)
These functions do not care about the contents of the managed memory areas and therefore should only be used with POD (plain old data) types.

Note: The function realloc () can also single-handedly take over the entire memory of a program by its parameters are set appropriately:

Code:
char* str = realloc (NULL, 100);/ / memory allocation 
...
realloc (str, 0);/ / Release memory
1.2 New and delete

For C + +, a new mechanism for memory management, which provides a better approach to the class. Unlike malloc () / free () the memory areas are regarded as objects of arbitrary classes, and now initialized with an appropriate constructor.
  • new T (...)
    enough reserved memory for an object of type T, and calls its constructor according to the specified parameter list (the parameter list can be removed also, then the default constructor uses)
  • delete p;
    calls the destructor of the object pointed to by p, and are then free the memory


Another advantage of new and delete is the fact that they can be overridden by the user. This requires either a global (for every new call) or static member function (for their own class, and all children) "operator new ()" or "operator delete ()", or the free memory can request. These operators work with unstructured data fields - the call to constructors and destructors are independently:

Code:
void* operator new(size_t s) 
(
void* p;
/ / Reserve memory for at least s bytes
return p;
)

void operator delete(void* p)
(
/ / Give free hand p
)
Both functions can obtain additional parameters of any type to implement the so-called "placement new". These parameters are by calling "new (values) T (ctor parameter) passed. The most common use cases of this are the specification of a destination address (an object to initialize in available memory) and the installation of debugging information (the MFC-operator DEBUG_NEW passes "as file name and program line to be able to track memory leaks).

Note: "operator new" and "operator delete" operate with bare areas of memory. The constructor and destructor of the object involved is called when operator new has come back before or operator delete is called.

Caution: Do not overcharge the global operators only if you are absolutely sure what you want to achieve. The global version of operator new and operator delete takes really ANY memory request within the program - and in contrast to klasseninternen operators can not here from the predefined memory access operators.

Another typical application for operator new and operator delete is to use a pool manager: in order to save computing time, the memory of precaution required in larger blocks, replaced and forwarded as needed to the program. From the program of shared memory is not returned to the system, but also for future requirements repealed.

1.3 New [] and delete []

In parallel with new and delete, playing with various objects, there are versions to manage whole object array:
  • new T [n]
    enough reserved memory for n objects of type T, and calls for an object whose default constructor
    Note: It is not possible to specify parameters for the constructor invoked
  • delete [] p;
    calls for all objects in the array after p destructor on the memory and are then free to
    Just like new / delete can be adapted to these functions by "be void * operator new [] (size_t s);" or "void operator delete [] (void * p); overwritten.

Note: new [] remember (just like malloc ()) internally the size of the allocated memory. However, there is no portable way to access this information.

Note: Despite the name similarity is not guaranteed that new / delete and new [] / delete [] are compatible with each other. So try NIE, the two mix with each other.

1.4 Other families

Many libraries define your own function pairs in order to request and release memory, eg VirtualAlloc () / manage Virtual Free () from the WinAPI, the memory in the "shared memory". All such families function in common is that they consist of at least two functions - one for requesting memory, one for release. Analogous to the malloc () family may be added other features that can reorganize the managed memory.

Because different memory strategies are not necessarily compatible with each other, you should always use the correct release function if you need more memory not yours. Therefore, it is usually a good idea to have a program on a strategy and set within this largely unsustainable. If that is not generally possible, should know where he is to his memory relate to each case are set for each pointer used.

Even in the standard library yet another function family is defined, which can be used to manage temporary storage:
  • get_temporary_buffer (num)
    allocates a temporary buffer for a maximum of num objects of type T and is a couple with its address and available disk space back to the
  • return_temporary_buffer (p)
    have requested a temporary buffer back to the system
These temporary buffer can be used within a function to provide large amounts of additional memory (eg, merge sort requires an auxiliary memory for its data). Make sure that the returned memory region may be less than requested.

Allocators



The containers of the STL default to new [] and delete [] to know the memory request for their elements. However, as it should be possible to switch to a different memory management, this behavior had been moved into so-called allocators - these can be interchanged, to use a different memory behavior. In addition, each container class as the last template parameter an indication of the allocator class. The default value for this is std:: allocator - this allocates memory using new [] and returns it with delete [] free again.

An allocator has to be used with the STL, provide some basic methods and definitions. If you write an allocator for its own purposes, the requirements may be lower.

2.1 management

The most important are the Allokatorklasse course, the functions that can manage the memory:
  • allocate (n, h = 0)
    Reserved uninitialized memory for n elements and returns it (eg by: operator new () or malloc ()). The optionally specified pointer h can be used to influence the storage strategy.
  • construct (p, val)
    Initializes the object pointed to by p, as a copy of val (usually via placement new)
  • destroy (p)
    Destroy the object pointed to by p (usually by direct destructor call)
  • deallocate (p, n)
    Is an area behind free p consisting of n elements (the parameter can be useful if the size of memory used can not be determined from the pointer itself) - The objects must first be destroyed
In addition, the allocator max_size the helper methods () that can specify the maximum size of a memory block (in elements) and address (ref) can be defined by the object at a given address.

Custom allocators are generally allocate the methods (), deallocate () and max_size () override to implement the storage strategy. construct () and destroy () are likely to differ only in the rarest cases, by the standard version:

Code:
template 
class Alloc
(
...
public:
void construct (pointer p, const T & val)
( New((void*) p) T val ();)
void destroy (pointer p)
(P-> ~ T ();)
...
);
2.2 rebind <>

In order to offer the ability to store various objects that contains a substructure Allokatorklasse called "Rebind. This is used as the associative container classes, which receive as an allocator template parameter , but need an internal TreeNode allocator <>.

This structure is pretty simple and serves only to "packaging" of the alternate type:
Code:
template 
class Alloc
(
...
public:
template
struct rebind
( typedef Alloc other;);
...
);
In this way, one can easily allocator for any data obtained by inspecting the structure:

Code:
template> 
class mycont
(
public:
//...
typedef A allocator_type;
typedef typename A:: rebind :: other ptr_allocator,/ / Pointer to compatible allocator
//...
);
2.3 Comparisons

Different allocators can be compared with the operators == and! =. These features will be released equality that two allocators are interchangeable - is requested by a memory on b again.

For example, building allocators, to delete the new /, are interchangeable, as is required for the delete-object call no tax. In contrast pool allocators are limited interchangeable because each allocator manages its own list of reserve memory.

Note: STL compatible allocators have to be interchangeable. reduce most of the comparison operators to a "return true reason," or return false; ".

2.4 Type definitions

Allocators also define some basic types used by the connected container classes continue:
  • value_type (T)
    the underlying value type
  • size_type (size_t) and difference_type (ptrdiff_t)
    two integer types for specifying memory sizes (unsigned) or "points-difference (signed)
  • pointer (T *) and const_pointer (const T *)
    Pointer to variable or constant objects
  • reference (T &) and const_reference (const T &)
    Reference to variable or constant objects
Note: STL compatible allocators is the award of these basic types defined (according to the above information).

Auxiliary functions for memory management



In addition to the standard library allocators defines some utility functions that can deal with non-uninitialized memory. These serve as a logical extension of memset () and memcpy ():
  • uninitialized_fill (beg, end, val)
    initializes the field (in the sense STL) [beg, end [with the corresponding number of copies of 'val'
  • (Uninitialized_fill_n beg, n, val)
    initializes the array behind 'beg' with n copies of 'val'
  • uninitialized_copy (beg, end, nbeg)
    initializes the array behind 'nbeg' with copies of the range [beg, end [
These functions can be also referred to in section 1.3 restriction bypass that new [] can only call the default constructor:
Code:
T * data =::operator new[] (n *sizeof(T)); 
unitialized_fill_n (data, n, T (4711));
...
delete[] data;
In an emergency, it is also possible to fill a new placement of uninitialized memory area with values:

Code:
* p myType = (myType *) malloc (sizeof(myType)); 
new(p) myType (params);
...
p-> ~ myType ();/ / direct call destructor
free (p);
(Note: should normally take place this construction rather directly to new / delete be used)

3.1 Rohspeicher iterators

Full Name:
Code:
template 
class raw_storage_iterator;
This Hilfsiteratoren can be used to an uninitialized memory as a target STL algorithm indicated. They translate into calls on assignments of the copy constructor placement new.

The template parameter of this class are, first, an output iterator that is used as the basis for the iterator arithmetic (usually enough for this one 'T *') and second, the data type used.

3.2 Functions of the C library

Unlike C + + interprets the C library typically anything in the memory areas, which it manages. Logically, obtained from malloc () a bunch of bytes with no internal structure. Then it depends on the programmer, as he fills the memory and interpreted.

To process this naked memory areas can be either the malloc () pointer obtained in the "right" type casting and processing of hand or some ready-made functions of the C library (memcpy (), memset (), etc.), or (with restrictions ) the string processing functions (strcpy (), strcat () to take, etc.) to help.

Note: Try to be better not to interpret bare memory than C + + objects. Most methods expect an existing basic structure and lead to undefined behavior if they get junk data set before.

Smart Pointer



The management of heap memory (no matter how it was obtained) on bare hands always suffer the same problem - no one can verify when and by whom the memory must be freed. Forget the last owner of the release shows a memory leak, because no one knows more about the existence of the memory block. If the memory released too early, you get access violations and undefined behavior if other users want to access the shared memory.

A solution to this problem is to make the pointer itself to instruct the memory management, instead of bare hands by so-called "smart pointer" is used. These differ in principle three different approaches to control the access and control:
  • Reference counting (boost:: shared_ptr, boost:: shared_array)
    The referenced object counts how many pointers point to it. And only when the last pointer from the object, separates the memory is freed.
  • Exclusive access (boost:: scoped_ptr, boost:: scoped_array)
    The address may not be transferred. The owner of the pointer and puts him in the end is also responsible to release him again.
  • Ownership transfer (std:: auto_ptr)
    There is always just a pointer that points to a memory (owner). When an assignment is the possession and passed the former owner loses access to the memory.


The operation and characteristics of the different smart pointer classes was some time ago in the article "Smart pointer" peter's declared.

Note: Normal smart pointer classes to a specific memory function (usually new / delete and new [] / delete []) defined and therefore expect that their managed storage "was created correctly."

The above smart pointer classes (except auto_ptr to find) into the Boost Library or TR1 extension of the standard. The auto_ptr class is part of the C + + Standard Library.

STL Container



Whom the whole burden of memory management is too complicated, it can also make it easier - and use the various classes of the STL containers. All the container take care of their own so to provide enough memory and to manage, so that the user no longer needs to worry about where their data is housed.

The usable container classes differ mainly from the perspective of memory in it, the strategy that they require memory and organize:
  • vector <> and basic_string <>
    use a contiguous area of memory that grows as needed
  • deque <>
    usually used a two-layered structure with multiple memory blocks hung together logically to be extended if needed or released
  • list <>
    uses a linked list, where each element has a pointer to predecessor and successor
  • set <>, multiset <>, map <> and multimap <>
    use a (balanced) binary tree of individually requested node into which they sort their elements

Note: This will of course depend on the type of allocator, where the containers maintain their memory.