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

Comments

comments powered by Disqus