You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The compiler will copy and paste them into the object CPP file.
CPP File
The compiler translates the CPP files into assembly code and then into
machine codewithout performing any linking operations.
Typically, there is one OBJ file per CPP file, but sometimes multiple
CPP files may result in a single OBJ file.
During the compilation phase, the compiler also optimizes your code.
OBJ File (Machine Code File, .obj in Windows, .o in Linux)
All OBJ files will be combined into a single file by the linker.
The C++ linker can perform certain optimizations, although its
primary role is focused on linking object files and libraries rather than
optimizing code.
Linking (Static Linking and Dynamic Linking)
Libraries
Static (.lib or .a) vs dynamic libraries (.dll or .so):
Linking with static libraries is faster than dynamic libraries because
the C++ linker performs optimizations during static linking.
Import libraries (xxxdll.lib files):
These files contain references to functions and symbols defined in the
xxx.dll files, facilitating linking at compile time.
This process ensures that the correct function signatures and addresses are
utilized, allowing the linker to ascertain their locations at runtime.
Import libraries are specific to Windows, while Linux integrates this
functionality directly into dynamic libraries.
Static Linking
Explanation
Static linking refers to the process of combining all necessary library code
into the final executable at compile time.
This results in a standalone binary that does not rely on external
libraries at runtime.
Characteristics
Self-contained:
The executable includes all the library code it needs.
No runtime dependencies:
Once compiled, it doesn't require the presence of shared libraries on the
target system.
Larger executable size:
The resulting binary is typically larger due to the inclusion of library
code.
No versioning issues:
The code is fixed at compile time, reducing concerns about library version
compatibility.
How It Works
Compilation:
The developer compiles the source code and links it against static library
files (usually .lib in windows, .a files in linux).
Linking:
The linker combines the object files with the static library code,
resolving all symbol references.
Executable creation:
The final output is a single executable file that includes all necessary
code.
Dynamic Linking (Implicit Linking and Explicit Linking)
Explanation
Dynamic linking allows a program to link to libraries at runtime rather
than at compile time.
This can improve modularity and reduce executable size.
Not need: ld and import libraries.
Characteristics
Reduced executable size:
Only references to shared libraries are included, keeping the executable
smaller.
Shared code:
Multiple programs can share the same library code in memory, saving
resources.
Version flexibility:
Libraries can be updated independently of the applications that use them,
allowing for easier updates and bug fixes.
Runtime dependencies:
The executable requires the appropriate shared libraries to be present at
runtime.
Implicit linking, also referred to as load-time dynamic linking is a form
of dynamic linking where required libraries are automatically loaded at
application startup.
It is a process whereby an executable is linked to shared libraries at
compile time.
This method allows the program to utilize functions and variables defined
in those libraries without incorporating their code directly into the
executable.
Characteristics
Automatic resolution:
The operating system automatically loads all required shared librarieswhen the application starts, even if they are not used.
Simplified development:
Developers include the DLL's import library (.lib file) in Windows or the
shared library (.so file) in Linux during compilation, simplifying the
linking process.
Single entry point:
The operating system resolves function calls from the shared libraries
automatically, allowing the application to use them seamlessly.
Version dependency:
If the required version of a shared library is missing or incompatible, the
application may fail to start.
How It Works in Windows
Linking with import libraries:
When compiling the application, the developer links against the DLL's
import library, which provides necessary metadata for the linker.
Creating the executable:
The resulting executable contains references to the required DLLs but does
not embed their code, reducing the executable's size.
Loading the DLLs:
Upon execution, the Windows loader reads the executable's headers to
identify the required DLLs and loads them into memory.
Resolving symbols:
The loader resolves function calls to the appropriate addresses in the
loaded DLLs, ensuring that the application can access the necessary
functions and variables.
Execution:
Control is then passed to the application's entry point, allowing it to
call functions from the linked DLLs, enabling shared access to the
library's functionality.
How It Works in Linux
Linking with shared libraries:
When compiling the application, the developer specifies shared libraries
using flags (e.g., -l for linking) in the compilation command, allowing
the linker to record these dependencies in the executable.
Creating the executable:
The resulting executable contains references to the shared libraries but
does not include their code.
This reduces the executable's size and enables shared access to the
libraries.
Loading the libraries:
Upon execution, the dynamic linker/loader (typically ld.so or ld-linux.so)
reads the executable's metadata to identify the required shared libraries
and loads them into memory.
Resolving symbols:
The linker resolves the function calls and variable references to the
appropriate addresses in the loaded shared libraries, allowing the
executable to use their functionality.
Execution:
Control is passed to the application's entry point, enabling it to call
functions and access data from the linked shared libraries.
Explicit Linking (Run-time Dynamic Linking)
Explanation
Explicit linking, also known as run-time dynamic linking, is a dynamic
linking form where the application manually loads libraries during execution.
It gives the programmer more control, allowing applications to dynamically
load shared libraries and access their functions during execution,
rather than linking them at compile time.
The programmers will use some specific functions to manage the dynamic
linking explicitly.
Characteristics
Dynamic control:
Developers can load libraries based on runtime conditions, providing
flexibility.
Lazy loading:
Libraries can be loaded only when needed, which can improve startup
performance.
Error handling:
Applications can handle scenarios where a library fails to load or is not
found.
How It Works in Windows
Load the DLL:
Use LoadLibrary( "library.dll" ) to load the desired DLL into the
process's address space.
Get function pointers:
Use GetProcAddress( handle, "function_name" ) to obtain pointers to the
functions within the loaded DLL.
Use the functions:
Call the functions using the retrieved pointers.
Unload the DLL:
Optionally, use FreeLibrary( handle ) to unload the DLL when it’s no
longer needed.
How It Works in Linux
Load the library:
Use dlopen( "library.so", RTLD_LAZY ) to load the desired shared library
into the process's address space.
Get function pointers:
Use dlsym( handle, "function_name" ) to obtain pointers to the functions
within the loaded library.
Use the functions:
Call the functions using the retrieved pointers.
Unload the library:
Optionally, use dlclose( handle ) to unload the library when it’s no
longer needed.
Differences Between gcc and g++
Language
1. gcc
Primarily the GNU C Compiler, but it can also compile C++ files if passed the
appropriate flags.
It defaults to treating source files as C code unless you specify C++.
2. g++
A specialized driver for C++ compilation.
It automatically links the C++ standard library and handles C++-specific
extensions.
Linking
1. gcc
When compiling C++ files with gcc, you must manually link the C++ standard
library (e.g., -lstdc++).
2. g++
Automatically links the C++ standard library without needing extra flags.
Header File Handling
1. gcc
When used with C, gcc processes headers as C-specific (using .h headers
for the most part).
2. g++
Treats headers as C++ files and supports C++ header files (like <iostream>
instead of <stdio.h>).
How to Compile One CPP File
Source files
Main.cpp
g++ Main.cpp
1. Usage
Preprocess, compile, and link Main.cpp to generate the executable file a.out.
2. Output
a.out
g++ Main.cpp -o Main.exe
1. Usage
Preprocess, compile, and link Main.cpp to generate an executable file.
The option -o specifies the name of the executable file.
2. Output
Main.exe
g++ -E Main.cpp -o Main.i
1. Usage
Preprocess Main.cpp to generate a preprocessed file.
The option -o specifies the name of the preprocessed file.
2. Output
Main.i
g++ -S Main.i
1. Usage
Compile the preprocessed file to generate an assembly file.
2. Output
Main.s
g++ -S Main.cpp
1. Usage
Preprocess and compile the source file to generate an assembly file.
2. Output
Main.s
g++ -c Main.s
1. Usage
Translate the assembly file into an object file.
2. Output
Main.o
g++ -c Main.cpp
1. Usage
Preprocess and compile the source file to generate an object file.
2. Output
Main.o
g++ Main.o -o Main.exe
1. Usage
Link the object file to generate an executable file.
2. Output
Main.exe
g++ -On Main.cpp -o Main.exe
1. Usage
Compile Main.cpp with optimization level n (where 0 ≤ n ≤ 3). Higher
values of n result in longer compile times and more aggressive
optimizations.
Compiler optimization levels adjust the types of optimizations applied during
compilation to balance speed, size, and behavior.
No optimization (-O0):
Keeps debugging information intact, with no optimizations.
Best for development and debugging.
Basic optimization (-O1):
Minor optimizations like dead code elimination improve speed slightly.
Suitable for builds needing small improvements without large compile times.
Moderate optimization (-O2):
Balances speed and code size with optimizations like loop unrolling and
better register usage.
Ideal for production builds.
Aggressive optimization (-O3):
Adds further optimizations like vectorization, yielding higher speed but
potentially increasing code size and instability.
Best for performance-critical applications, though requires careful
testing.
Optimize for size (-Os):
Prioritizes a smaller binary, omitting some space-costly optimizations.
Useful for memory-constrained environments.
Fastest optimization (-Ofast):
Maximizes speed with non-standard optimizations, such as unsafe math
operations, reducing standard compliance.
Suitable for performance-critical applications that can relax stability or
portability requirements.
Generally, -O2 and -Os are widely used for balanced builds, while -O3
and -Ofast cater to performance-intensive needs where behavior can be less
predictable.
Improves startup performance by reducing unnecessary dynamic library loading.
Reduce the size of executable without linking to unnecessary libraries.
Safe in most cases, but issues may arise if the program relies on implicit
behavior from unused libraries, such as: dynamic binding and implicit
conversions.
3. Some Behaviors
Initialization code:
Some libraries might initialize global variables or perform certain startup
tasks when loaded, even if their functions aren’t called directly in the
code.
Static object constructors:
Some libraries may have static objects whose constructors are invoked
automatically when the library is loaded. These constructors might perform
important setup tasks.
Unreferenced functions:
Some functions or symbols might be referenced implicitly by the runtime or
other parts of the library, even if not directly invoked by the main
application code.
g++, clang++ and mscv++
Explanation
There are various compilers available. Which compiler is the best? The
choice of compiler depends on the library and system being used. Most
libraries are optimized for specific compilers. Even when libraries serve the
same purpose, their implementations are often tailored to particular
compilers. Furthermore, even if the compilers are of the same type but differ
in version or system, the compilation results may still vary.
For example, libstdc++ and libc++ are both implementations of the C++
Standard Template Library (STL). However, when clang++ is used with
libstdc++ to compile, the execution performance can be very slow. In
contrast, using libc++ with clang++ often results in significantly faster
execution. An example code snippet is shown below.
Additionally, different systems configure different default libraries. For
instance, Linux systems typically configure libstdc++ as the default
library, whereas Apple systems set libc++ as the default. Modifying these
default settings are complex and may lead to errors.
Therefore, when choosing a compiler, first check which system you are using.
Then, if possible, verify the library requirements for your project.
Tips, in most cases:
Linux - gcc/g++
Macos - clang/clang++
Win - mscv/mscv++
Code Examples
#include<iostream>
#include<random>usingnamespacestd;
mt19937::result_type seed = 0;
mt19937 gen( seed );
uniform_real_distribution< double > distr( 0, 1 );
intmain() {
double s = 0;
for( auto i = 1; i <= 100000000; ++i ) {
s += distr( gen );
}
std::cout << s;
return0;
}
Notes
When compiling C/C++ code, the -pedantic-errors compiler argument is
strongly recommended.
This argument disables all compiler extensions not specified in the C/C++
standards.
Compiler extensions are features added by compiler developers to enhance
compatibility with other versions of the language (e.g., C99) or for
historical reasons, and they are always enabled by default.
We had better add a newline to the end of code files if it is missing, as
this is a requirement of the C++ standard for compliance with pedantic
rules."