about
C++ Models
12/17/2022

C++ Models

Compilation, exectution, memory storage


Compilation Model

Figure 1. Compilation Models
C++ compiles each *.cpp file independently, and does not save type information when compiling more than one. Each *.cpp file and all of its included *.h files are called a compilation unit.
The operation of a C++ Compiler is shown in Figure 1. Its first action is to build an intermediate source code file with a preprocessor by replacing each #include statement in source code with the entire code of the included file. Included files are really included in that source text. The preprocessor also expands any macros or uses them to set compiler directives, e.g., for #pragma once.
The Compiler then consumes the intermediate source file and either compiles to an object file (*.obj), static library (*.lib), or dynamic link library (*.dll), or, if there are compilation errors, it simply emits error messages.
The results of these compilation output files are processed by a Linker. When program code makes calls or transfers to code in the same compilation unit, the compiler assigns addresses based on the code it has laid out. However, if the code makes calls into another compilation unit, then the compiler doesn't have an address, and so makes an entry in a table of unresolved addresses.
The job of the Linker is to resolve these addresses. It can do that, because it does not execute until all of the compilation units that target a specific execution image are compiled, so it has all the addresses it needs and proceeds to resolve the unknowns.
That results in a runnable execution image. However, that is not the end of the story. The build process may have defined dynamic link libraries which get loaded during execution. It is the job of the Loader to start the execution image, and bind, at run-time, any dlls that the program needs.

Program Execution Model:

Figure 2. Program Execution Model
When execution of a C++ program begins, initialization code generated by the compiler runs, then the thread of execution enters main, with any arguments defined on the command line. Main entry creates a stack frame - a block of allocated stack memory - that holds input arguments, any local data defined by main, and the return value, used to indicate success or failure to the operating system.
Should main call a function, another stack frame is allocated for that function, and if that function calls another, it too allocates a stack frame. Stack frames are allocated, as scratch-pad memory, for every scope entered by the thread of execution. When the thread leaves that scope the allocated memory becomes invalid. The next time a stack frame needs allocation, the invalid memory is likely to be part of that allocation.
Heap memory can be allocated by a program's code with a call to new, and deallocated with a call to delete. Malloc and Free serve the same purpose in a C language program.
Input and output operations defined by a C++ program are handled with streams - iostreams cin and cout for the screen and console and fstreams ifstream and ofstream for files. Error and logging are handled by cerr and clog. All of these stream objects are constructed as global objects by the initialization code that runs before main is entered, and are available anywhere in the program code.
The handles stdin, stdout, stderr, and stdlog are used by C programs. They are references to the program's input and output channels, attached to screen and console. The program can define other handles for channels to files defined by the program or discovered in the file system.

Memory Model:

Figure 3. Program Memory Model
Figure 3. shows details of the C++ Memory Model. There are three types of memory: static, stack, and heap, each with their own lifetime models.
Anything in static memory is defined, and has coherent values, for the lifetime of the program. That includes all program code, global data, and static local data. Static local data is defined inside functions and qualified by the keyword static. Local static data is initialized on the first entry to the function where defined, but does not get re-initialized on subsequent entries, so static data can save information that persists between function calls.
Stack memory holds information defined by each program scope, e.g., a block of code surrounded by braces "{" and "}". The life time of a stack allocation begins when the thread of execution enters the scope, and ends when it leaves the scope. Then the allocation becomes invalid, the memory is returned to the memory manager for reuse, and may be allocated for the next scope. The call stack you see in a debugger running C++ code is just the set of stack allocations shown in Figure 2. and Figure 3..
Heap memory is provided to a running program by the operating system. A default heap is created when a C++ program begins execution. The program code allocates heap space by using calls to operator new and deallocates with a call to delete. So the life time of a heap object starts with it's allocation with new, and ends with deallocation with delete.
  Next Prev Pages Sections About Keys