C++

C++ - Sizing and Type Conversion

Memory Alignment, Sizeof

Posted by Rico's Nerd Cluster on January 17, 2023

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.

Source

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
      
  • 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) vs static_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* vs unsigned char*
    • Low-Level Pointer Conversion: reinterpret_cast allows arbitrary type conversion between pointer types, essentially bypassing type safety. USE WITH CAUTION