C++ / a working model

01 / 163   ·   C++11   ·   7 min

sizeof: Measure Object Representation, Not Runtime Content

Keep this sentence

sizeof queries the object-representation size of a static type, in C++ bytes, including necessary padding. It often looks like a function call but is an operator whose operand is not evaluated; results for arrays, references, and pointers must be understood separately.

In this lesson
  1. Establish the unit of measurement first
  2. Unevaluated, yet still type-checked
  3. Arrays keep their bounds; pointers carry no capacity
  4. Example
  5. Exercise

Establish the unit of measurement first

sizeof(T) returns the number of bytes occupied by the object representation of a complete T object; the result type is std::size_t. The byte in this measurement is not a fixed eight bits: sizeof(char) must be 1, and the number of bits per byte is given by CHAR_BIT, which is at least 8. Do not write a particular machine's four-byte int or eight-byte pointer as if those widths were language guarantees.

The result for a struct includes necessary padding between members and at the end, so it is not necessarily equal to the sum of the member sizes. A complete object of an empty class also has nonzero size; that does not mean an empty base-class subobject must occupy an extra byte. When answering a size question, state the standard guarantees first, then the concrete layout of the target ABI.

Treat sizeof as a question about representation of a type, not about the values currently stored in an object. Padding, alignment requirements of the ABI, and the distinction between a complete object and a subobject all affect the number you get. Keep the standard's portable claims separate from the layout you observe on one platform.

Unevaluated, yet still type-checked

sizeof(++n) does not increment n, because the operand is in an unevaluated context. The compiler still performs name lookup and type checking; an undeclared name, a function type, an incomplete type, or taking the size of a bit-field directly does not become legal for that reason. Standard C++ does not provide C-style variable-length array rules.

Sizing an expression looks at the static type, not the dynamic type of a runtime object. Viewing a derived object through a base-class reference yields the base-class size. Using sizeof on a reference type itself yields the size of the referred-to type; that result does not tell you how the implementation stores the reference internally.

Unevaluated does not mean unchecked. The operand must still name a complete object type that sizeof is allowed to apply to. Because the static type controls the result, a base reference cannot reveal how large the derived object is, and a reference type is not a second, distinct size you can use to inspect the reference's hidden representation.

Arrays keep their bounds; pointers carry no capacity

The operand of sizeof does not undergo array-to-pointer conversion, so for a local array a, sizeof(a) / sizeof(a[0]) yields the element count. The example demonstrates both the array-size relationship and unevaluated behavior, without printing any platform-dependent type sizes.

sizeof on a pointer measures only the pointer object; it cannot tell whether the pointer points to one element, an array, or dynamically allocated storage. Real interfaces should pass length along with the data, for example by using a container's size, or by accepting a span in C++20, rather than trying to recover capacity from a pointer.

Keep the array type in view for as long as you need the bound. Once the array has decayed, the pointer no longer carries how many elements were there. Length belongs in the interface: a container member, an explicit count, or a span, not a guess reconstructed from pointer width.

Pitfalls

  • The type form of sizeof requires parentheses; sizeof a + b means (sizeof a) + b, not sizeof(a + b).
  • sizeof(std::string) reflects only the string object itself, not any character storage it may own in dynamic memory.

Run an example

Minimum C++11 · complete program · Download .cpp

#include <cassert>
#include <cstddef>
#include <iostream>

int main() {
    int n = 4;
    int values[3] = {1, 2, 3};
    const std::size_t measured = sizeof(++n);
    static_assert(sizeof(char) == 1, "C++ byte");
    static_assert(sizeof(values) == 3 * sizeof(int), "array size");
    static_assert(sizeof(int&) == sizeof(int), "reference target");
    assert(n == 4);
    assert(measured == sizeof(int));
    std::cout << n << ' ' << sizeof(values) / sizeof(values[0]) << '\n';
}

Compile locally

g++ -std=c++11 -Wall -Wextra -Wpedantic -pthread basics-sizeof.cpp -o example && ./example

Expected result

4 3

CHECK YOUR UNDERSTANDING

Close the answer. Explain it.

Given int a[7]{}; int* p = a; int& r = a[0];, which sizeof results are guaranteed to be multiples of sizeof(int), and can sizeof(p) be used to obtain the array length?

Show a reference answer

sizeof(a) is necessarily 7 * sizeof(int); sizeof(r) is necessarily sizeof(int). sizeof(p) is the size of int*, unrelated to the array having seven elements. Take the element count where the array type is still visible, or carry the length explicitly; do not infer allocated size from pointer size.

Check the sources

Drafts and official chapters change. The version mark is only the example’s minimum.

Back to the catalog