Memory Alignment
C++ struct / class does memory alignment. Here’s an illustrative example:
1
2
3
4
5
struct LocalizationData {
float x, y; // Each float is 4 bytes
bool r; // bool is 1 byte
};
sizeof(LocalizationData); // see 12 bytes, without memory alignment, it should be 9 bytes
- float (4 bytes): Typically requires 4-byte alignment.
- bool (1 byte): Typically requires 1-byte alignment, but it can also be padded to match the alignment of the structure.
With alignment, and with proper offsets, we can access data in a more modular way. Otherwise, there could be performance penalties.
To enable and disable memory alignment, one can:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
using namespace std;
// remove memory alignment of bool and float. otherwise, more memory is padded to bool
#pragma pack(1)
struct LocalizationData {
float x, y;
bool r;
};
#pragma pack() // Restore default alignment
struct LocalizationData2 {
float x, y;
bool r;
};
int main()
{
std::cout<<sizeof(LocalizationData)<<std::endl; // see 9 because memory alignment is disabled
std::cout<<sizeof(LocalizationData2)<<std::endl; // see 12 because memory alignment is enabled
auto d2 = LocalizationData2();
std::cout<<sizeof(d2.r)<<std::endl; // see 1
return 0;
}
Alignment and Padding:
- To satisfy alignment requirements, compilers may add padding between variables or at the end of the struct.
- For example, in the struct above, a (4 bytes) might leave a 4-byte padding before b (8 bytes) to align b on an 8-byte boundary.
- Inheritance: for classes with inheritance, memory layout may include hidden padding or vtable pointers (if virtual functions are used). For non-inherited plain structs, the layout is straightforward.
Miscellaneous Size Issues
- Does static constexpr Change the Object’s Size?
- No.
static constexpr
variables belong to the class itself, not part of the instance. E.g,1 2 3 4
struct Example { static constexpr int static_var = 42; // Shared by all objects, not part of any instance int a; }; // 4bytes, only for a
- No.
sizeof(object_or_type)
returns the number of bytes a type or object consumes.
Type Conversion
reinterpret_cast
vs static_cast
reinterpret_cast<char*>(unsigned_char_ptr)
vsstatic_cast<char*>(unsigned_char_ptr)
(which doesn’t work)static_cast
converts types where conversion is well defined. But it does NOT support conversions between unrelated pointer types.char*
vsunsigned char*
- Low-Level Pointer Conversion: reinterpret_cast allows arbitrary type conversion between pointer types, essentially bypassing type safety. USE WITH CAUTION