The C++ build process is roughly as thus:
- All .cpp (and possibly .c) files are compiled:
- First the preprocessor processes the file, it (as required):
- Parses and processes all
#defined macros, storing their definitions in a symbol table (a string to string dictionary). - Parses and processes all
#if,#elifand#endifmacros, thus performing conditional compilation. - Parses and processes all
#included header (.h) files, which includes inserting the contents of the header file into the point at which it was #included (or performs a process that achieves the equivalent effect). - Parses and processes all
#pragmas, implementing their compiler-specific behaviour.- Note that the most common and widely supported
#pragmais#pragma once, which acts as an alternative to include guards. - Other
#pragmaexamples include#pragma omp, which implements 'Open Multi-Processing'
- Note that the most common and widely supported
- Parses and processes all
- The resulting preprocessed C++ source code (now free of all preprocessor directives and macros) is parsed and processed according to the rules of the C++ language. This phase results in the generation of ‘object files’ (file extensions and formats vary by OS and by compiler, e.g.
.ofiles) which contain ‘object code’ (specially formatted machine code) and debugging symbols. This phase typically includes multiple optimisations. - An optional phase of optimisation
- First the preprocessor processes the file, it (as required):
- All the object files resulting from the compilation of the C++ code are ‘linked’ together by the linker, thus generating an executable file, which again varies by compiler target.
- On Windows this is typically a
.dllor an.exe - For code targetting an AVR/Arduino device this, the generated result is actually an
.elffile - an executable format popularly used by Linux
- On Windows this is typically a
- Mostly specific to embedded systems, the resulting executable format is stripped of all unnecessary information (debugging symbols, function relocation information, et cetera) and transformed into a blob of raw data. For an AVR/Arduino device this is a typically a
.hexfile, whilst for ARM this is a.binfile
For a more abstract, technical and/or specific explanation, see:
https://en.cppreference.com/w/cpp/language/translation_phases
Also relevant is the difference between declarations and definitions, and the ‘one definition rule’:
https://en.cppreference.com/w/cpp/language/definition