C++ Fundamentals
Basic Concepts
Datais any information processed or stored by a computer.- A
Valueis a specific piece of data (e.g.42,'A',3.14). - An
Objectis a region of storage (memory) with a type and a value. - A
Variableis a named object used to store data. Initializationis the process of giving an initial value to an object or variable.Literals: fixed values like 42, 3.14, ‘A’, “Hello”, true, nullptr.Operators: symbols that act on values (+ - * / %, == != < >, && || !, = += -=, etc.).- An
Expressionis a combination of operators, constants and variables that evaluate to a value. - A
functionis a reusable block of code that performs a specific task.
1. Initialization
C++ provides several ways to initialize objects. Each form has different semantics and use cases.
1.1. Default Initialization
int a;
T object;
new T;
Performed when an object is created without any initializer.
- Built-in types: uninitialized
- Class types: default constructor is called
1.2. Value Initialization
int a{};
T();
new T();
T object{};
T{};
new T{};
/// @brief Also used in constructors
Class::Class() : member() {}
Class::Class() : member{} {}
Performed when an object is created with an empty initializer.
- Built-in types: zero-initialized
- Class types: default constructor is called
1.3 Direct Initialization
T object(arg);
T object(arg1, arg2);
T object{arg}; // since C++11
new T(args...);
static_cast<T>(other);
/// @brief Used in constructors and lambdas:
Class::Class() : member(args...) {}
[arg]() {};
Initializes an object using explicit constructor arguments.
1.4 Copy Initialization
T object = other;
f(other);
return other;
throw object;
catch (T object);
/// @brief Also used for arrays
T array[N] = { /* values */ };
Initializes an object from another object or expression.
1.5 List Initialization (since C++11)
/// @brief Direct List Initialization
T object{arg1, arg2};
new T{arg1, arg2};
Class::Class() : member{arg1, arg2} {}
/// @brief Copy List Initialization
T object = {arg1, arg2};
return {arg1, arg2};
function({arg1, arg2});
/// @brief Designated Initializers (since C++20)
T object{ .des1 = arg1, .des2 = arg2 };
T object = { .des1 = arg1, .des2 = arg2 };
Initializes objects using brace-enclosed initializer lists { }.
1.6 Aggregate Initialization
T object = {arg1, arg2};
T object{arg1, arg2}; // since C++11
/// @brief Designated initializers for aggregates (since C++20)
T object = { .des1 = arg1, .des2 = arg2 };
T object{ .des1 = arg1, .des2 = arg2 };
Initializes aggregate types (no user-defined constructors).
2. Fundamental Data Types
Memory can only store bits. Data type help compiler and CPU take care of encoding the value into the sequence of bits.
- Fundamental Data Types
- Compound Data Types
3.1 Primitive Type
| Types | Category | Meaning | Example |
|---|---|---|---|
| float, double, long double | Floating Point | a number with a fractional part | 3.14159 |
| bool | Integral (Boolean) | true or false | true |
| char, wchar_t, char8_t (C++20), char16_t (C++11), char32_t (C++11) | Integral (Character) | a single character of text | ‘c’ |
| short int, int, long int, long long int (C++11) | Integral (Integer) | positive and negative whole numbers, including 0 | 64 |
| std::nullptr_t (C++11) | Null Pointer | a null pointer | nullptr |
| void | Void | no type | n/a |
sizeofused to get the size of a type in bytes. (pointer has 4 or 8 bytes based on the arch)fixed-width integersare the set of integer types that are guaranteed to be the same size on any architecture#include <cstdint>fast integers: guarantee at least # bits, but pick the type that the CPU can process fastest (even if it uses more memory).least integers: guarantee at least # bits, but pick the type that uses the least memory (even if it’s slower).
size_tvsstd::size_tis an unsigned integral type that is used to represent the size or length of objects. (<stddef.h>,) scientific notation e/Eused to present the times 10 to the power of the equation. e.g. (e.g. 5.9722 x 10²⁴ -> 5.9722e24)Infrepresents infinity. Inf is signed, and can be positive (+Inf) or negative (-Inf). (5/0)NaNstands for “Not a Number”. (mathematically invalid)
3.2. Char And String
- A
charvariable are interpreted as anASCII character. 't': Text between single quotes is treated as a char literal, which represents a single character."text": Text between double quotes (e.g. “Hello, world”) is treated as a C-style string literal, which can contain multiple characters.
4. Constant
- A
constantis a value that is not be changed during the program’s execution. There are two types of constants:- Named constants: are constant values that are associated with an identifier.
- Constant variables
- Macros with substitution text
- Enumerated constant
- Literal are constant values that are not associated with an identifier.Literals are values that are inserted directly into the code.
- Named constants: are constant values that are associated with an identifier.
4.1. Named constants
const double gravity = 9.8; ///< Const variable
#define MY_NAME "Phong" ///< Object-like macros with substitution text
enum Color {
RED, // Assigned 0
GREEN, // Assigned 1
BLUE = 5, // Manually assigned 5
YELLOW // Assigned 6 (5 + 1)
}; ///< Enumerated constant
4.2. Literals
return 5; ///< 5 is an integer literal -> type: int
bool myNameIsAlex { true }; ///< true is a boolean literal -> type: bool
double d { 3.4 }; ///< 3.4 is a double literal -> type: double
cout << "Hello, world!"; ///< "Hello, world!" is a C-style string literal -> type: const char[14]
cout << 5.0 << '\n'; ///< 5.0 (no suffix) is type double (by default)
cout << 5.0f << '\n'; ///< 5.0f is type float
Type of a literalis deduced from the literal’s value.Literal suffixesused to explicitly declare the type for a literal.
4.3. Numeral Systems (decimal, binary, hexadecimal, and octal)
- Numeral system literals:
- Decimal (no prefix, 42)
- Binary (0b101010)
b - Hexadecimal (0x2A)
x - Octal (052) - all represent the same value.
- Can change the output format via use of the
std::dec, std::oct, and std::hexI/O manipulators: - Can define a
std::bitsetvariable and tellstd::bitsethow many bits we want to store.int bin{}; // assume 16-bit int bin = 0x0001; // assign binary 0000 0000 0000 0001 to the variable bin = 0b1; // assign binary 0000 0000 0000 0001 to the variable bin = 0b11; // assign binary 0000 0000 0000 0011 to the variable cout << std::bitset<4>{ 0b1010 } << '\n'; // create a temporary std::bitset and print it // C++14: quotation mark (‘) as a digit separator int bin { 0b1011'0010 }; // assign binary 1011 0010 to the variable long value { 2'132'673'462 }; // much easier to read than 2132673462
4.4. Constant Expression
-
Constant expressionsis the expressions whose values can be fully determined at compile time. -
constexpris used to declare compile-time constants -
Benefits:
- Safer code
- Optimizations
- Compile-time evaluation
-
constexprfunction is is a function that is allowed to be called in a constant expression, can be evaluated at compile time or runtime. -
constevalfunction is a function that must evaluate at compile-time.constexpr int square(int x) { return x * x; } consteval int cube(int x) { return x * x * x; } constexpr int a = square(5); // compile-time int b = square(5); // runtime allowed constexpr int c = cube(5); // OK // int d = cube(5); // Error if not compile-time
5. Operators And Bit Manipulation
5.1. Operators
- Refer : https://www.learncpp.com/cpp-tutorial/operator-precedence-and-associativity/
Increment/decrement:++x: increment x, then return xx++: copy x, then increment x, return the copy
Comma:(x, y): Evaluate x then y, returns value of y
5.2. Bit manipulation.
- To define a set of bit flags, use
uint8/16/32… orstd::bitset - Refers: https://www.learncpp.com/cpp-tutorial/bit-flags-and-bit-manipulation-via-stdbitset/
// For unsigned integers (when no overflow occurs): // x << n == x * 2ⁿ // x >> n == x / 2ⁿ uint8_t x = 5; // 0b00000101 x << 1; // 0b00001010 = 10 x << 2; // 0b00010100 = 20 uint8_t x = 20; // 0b00010100 x >> 1; // 0b00001010 = 10 x >> 2; // 0b00000101 = 5
6. Control Flows
6.1. Constexpr if statement (C++17)
- Condition will be evaluated at runtime.
void main() { constexpr double gravity{ 9.8 }; if constexpr (gravity == 9.8){ // now using constexpr if std::cout << "Gravity is normal.\n"; } else { std::cout << "We are not on Earth.\n"; } }
6.2. Switch fallthrough and scoping
- The
[[fallthrough]]attribute modifies a null statement to indicate that fallthrough is intentional (and no warnings should be triggered). - Initialization is not allowed before case labels because control flow in a switch may jump over the initializer, leaving the variable uninitialized.
- Declarations without an initializer are allowed before case labels because they only reserve space for the variable in the function’s stack frame (decided at compile time). No runtime initialization code is generated, so nothing can be “skipped” by jumping to a case.
void main() { int x = 2; switch (x) { int a; // allowed (no initializer, just reserves space) // int b{5}; // not allowed (initializer may be skipped) case 1: a = 10; // safe: 'a' exists, we assign here std::cout << "Case 1, a = " << a << '\n'; [[fallthrough]]; // intentional fallthrough to case 2 case 2: a = 20; // reassign std::cout << "Case 2, a = " << a << '\n'; break; default: std::cout << "Default case\n"; break; } }
7. printf/snprintf
printf() prints formatted output to the console or standard output.
snprintf() formats output and stores it in a buffer while limiting the number of bytes written, helping prevent buffer overflows.
printf("data %d", value);
uint8_t buffer[100];
sprintf((char*)buffer, "data %d", value);
printf((char*)buffer);
Integer
| Data type | Specifier |
|---|---|
int8_t / signed char |
%hhd |
uint8_t / unsigned char |
%hhu |
int16_t / short |
%hd |
uint16_t / unsigned short |
%hu |
int32_t / long |
%ld |
uint32_t / unsigned long |
%lu |
int64_t / long long |
%lld |
uint64_t / unsigned long long |
%llu |
Floating point
| Data type | Specifier |
|---|---|
float |
%f |
double |
%f |
long double |
%Lf |
%e-> scientific notation%g-> auto select%for%e
Char / String
| Data type | Specifier | Notes |
|---|---|---|
char |
%c |
single character |
char* / String |
%s |
null-terminated string |
Pointer / Address
| Data type | Specifier | Notes |
|---|---|---|
void* |
%p |
memory address, hex |
Hex / Octal / Binary
| Data type | Specifier | Notes |
|---|---|---|
| unsigned int | %x / %X |
hexadecimal |
| unsigned int | %o |
octal |
| Arduino only | %b |
binary |
Flags, Width, Precision
%-10d-> left-justify, width 10%010d-> pad with zeros, width 10%.2f-> 2 decimal digits%*d-> dynamic width
8. Memory Functions
The
memset: Fill a block of memory with the same byte value.memcpy: Copy a block of memory from source to destination.memmove: Copy memory safely when source and destination overlap.memcmp: Compare two blocks of memory.