home page -> teaching -> data structures and algorithms -> memory model

Memory model

Basics

A data type in c++ and, generally, in most compiled languages, is a template for interpreting the content of a zone of memory.

Note that the memory is an array of bytes, with no markings where a variable begins or ends.

In order for the compiler to produce code that performs an operation to a variable, the compiler must know two things:

Address of the variable = address of the first byte of the variable

The size of a variable (how many bytes the variable occupies) may be known (constant) or unknown (variable) at compile time. There severe limitations on where and how an unknown size variable can be used.

Constructing data types

Basic types are integers of fixed length (fixed at compile time): 8-bit integers, 16-bit, 32-bit, 64-bit.

There are 3 ways to combine simpler types to form a more complex type: arrays, records (structs), and pointers.

Arrays

An array is a sequence of elements of the same type, situated one after the other in memory.

The address of element i is address(array) + i*size(element).

The (memory) size of the array is size(array) = nr_elements * size(element).

Therefore:

As a consequence, variable-size variables, including arrays with variable number of elements, cannot be used as array elements.

C++ examples:

  int a[10]; // ok. Size is 10*sizeof(int)
  int b[];   // error. Cannot declare a variable of variable size.
  void f(int a[]);  // ok. The function receives the address of c
  void g(int a[][10]); // ok. Array of fixed-size arrays
  void h(int a[10][]); // error. Array of variable-size array is not ok.

Records (structs)

Records group together several fields of possibly different types.

The address of each field is the address of the record plus the sum of the sizes of the fields before it.

All fields (with the possible exception of the last one) must be of compile-time known (constant) size.

Pointers

A pointer is a memory address. In principle, a pointer is simply a fixed size integer (32 or 64 bits); however, some computer architectures require more special pointers (for example, on 8086 real mode, pointers are formed of a segment and offset).

There is no difference between pointers to different types; for example, a pointer to a char is identical to a pointer to a long. However, taking the address of a variable of one type, and then interpreting the memory at that address as if it were someother type, is almost always a bad idea; for that reason, the compiler require each pointer variable to have an associated type for the pointee.

Variables

There are 3 kinds of variables:

Static variables exist for the whole execution of the program. They are allocated by the compiler, on the data segment. The data segment behaves like a single (huge) variable of record type, with each global variable of the program behaving as a fild of it.

As a consequence, static (global) variables must be of a compile-time constant size.

Automatic variables are the local variables declared inside a function, plus the parameters of that function. They exist for the duration that function is active - from the moment it is called till the moment it returns to the caller. This also means that, for a recursive function, a single automatic variable exists simultaneaously in multiple instance, one for each invocation of the function.

All automatic variables of a function are allocated as fields in the so-called activation record of the function. The address of the activation record is stored in a fixed register of the CPU (typically, the bp registers, on i386). There is a global execution stack that holds all the activation records of active functions. When a function is called, the activation record of that function is added to the stack, and when the function returns, the activation record is removed from the stack.

Again, automatic variables must have constant size.

Dynamic variables are created explicitely at runtime by calling, for example, new. The runtime library keeps track of allocated objects, so that it doesn't allocate the same memory area to distinct variables. Freeing memory is done either explicitely by the program, or, through garbage collection, when the runtime allocator detetmines that the variable is no longer accessible.

Since dynamic variables are created at runtime, there is no way to associate them to compile time names. So, dynamic variables cannot have names, and the only way to identify them is through pointers.

The size allocated to a dynamic variable is requested when the variable is created. Therefore, they don't need to have compile time constant size. However, once allocated, dynamic variables keep the same size.

Radu-Lucian LUPŞA
2016-03-13