When one wants to understand the memory layout of structures and classes, the C/C++ operators sizeof and offsetof are very useful. However, when large C++ class hierarchies are involved, using these operators becomes tedious. Luckily, Clang has a very handly command-line flag to dump object layouts in a useful manner. This flag is somewhat hidden since it's only accepted by the Clang front-end (the one you get when you pass -cc1 to clang) and not the gcc-compatible compiler driver (the one you get when simply executing clang).
Consider this code, for example:
class Base {
protected:
int foo;
public:
int method(int p) {
return foo + p;
}
};
struct Point {
double cx, cy;
};
class Derived : public Base {
public:
int method(int p) {
return foo + bar + p;
}
protected:
int bar, baz;
Point a_point;
char c;
};
int main(int argc, char** argv) {
return sizeof(Derived);
}
To see the layout, run clang -cc1 -fdump-record-layouts myfile.cpp. It will produce a separate report for each class and struct defined, but the most interesting one is for class Derived:
*** Dumping AST Record Layout
0 | class Derived
0 | class Base (base)
0 | int foo
4 | int bar
8 | int baz
16 | struct Point a_point
16 | double cx
24 | double cy
| [sizeof=16, dsize=16, align=8
| nvsize=16, nvalign=8]
32 | char c
| [sizeof=40, dsize=33, align=8
| nvsize=33, nvalign=8]
(the above is the output of Clang 3.2 running on 64-bit Linux)
We can see the layout of Derived objects, with the offset of every field (including the fields coming from base classes) in the left-most column. Some additional information is printed in the bottom - for example, sizeof - the total size, and dsize - data size without tail padding.
If we make method virtual in the Base and Derived classes, the size of the virtual-table pointer is also accounted for:
*** Dumping AST Record Layout
0 | class Derived
0 | class Base (primary base)
0 | (Base vtable pointer)
0 | (Base vftable pointer)
8 | int foo
12 | int bar
16 | int baz
24 | struct Point a_point
24 | double cx
32 | double cy
| [sizeof=16, dsize=16, align=8
| nvsize=16, nvalign=8]
40 | char c
| [sizeof=48, dsize=41, align=8
| nvsize=41, nvalign=8]
I'll wrap up with a tip about using clang -cc1. Since this isn't the compiler driver, it won't go look for standard headers in the expected places, so using it on realistic source files can be a pain. The easiest way to do it, IMHO, is to run it on preprocessed source. How your source gets preprocessed depends on your build process, but it's usually something like:
clang -E [your -I flags] myfile.cpp > myfile_pp.cpp