Try to compile and run this program with your C compiler and with your C++ compiler:

#include <stdio.h>

struct empty {};

int main()
{
  printf("nothing is %d\n", sizeof(struct empty));
}
How big should struct empty, which contains nothing, be?

C gets this right: bytes. But C++ requires it to take 1 byte. That's so that various object identity weirdnesses work out correctly in more complicated cases. But for struct empty it's just plain crazy...

As Gorgonzola points out, it gets crazier than this. Suppose I had to write a class that implements 10 interfaces. Each interface is an empty base class, so it has size 1. Does it follow that the derived class needs at least 10 bytes, just to store 10 copies of nothing?

No. The empty base-class optimization is to remove all storage for empty base classes. This is required for any reasonable quality of implementation, but also adds to the craziness. It is commonly assumed that if I declare

class C: public A, protected B { ... };
then sizeof(C) >= sizeof(A)+sizeof(B). Except, of course, that if A, B and C are all empty, this may quite simply fail!

In C++, derived classes can be smaller than the sum of their base classes. But, for obvious reasons, a class cannot be smaller than the sum of its contents -- even if its contents are all empty.

Something interesting in C (but not C++):

#include <stdio.h>

struct empty {};

int main (void)
{
    struct empty a[20], *i;

    for(i=a; i<=&a[19]; ++i) {
        printf("%p\n", i);
    }
    return 0;
}

This program will, if your compiler does not pad struct empty, be an infinite loop. Pointer addition in C and C++ works kind of like the following, where `type' can be any addressable type:

type *ptr_add(type *base, ptrdiff_t offset) {
    unsigned long addr, b = (unsigned long)base;
    
    addr = b + offset*sizeof(type);
    return (type *)addr;
}

In the first program we had ++i, which in that context means i = ptr_add(i, 1). Because sizeof(struct empty) == 0, offset*sizeof(type) == 0 and i remains unchanged. ++ applied to a pointer to struct empty will do nothing.

Even more weird:

#include <stdio.h>

struct empty {};

int main(void) {
    struct empty x[20];

    printf("%d\n", &x[10] - x);
    return 0;
}

Pointer differences work like:

ptrdiff_t ptr_diff(type *minuend, type *subtrahend) {
    unsigned long a = (unsigned long)minuend;
    unsigned long b = (unsigned long)subtrahend;
    long diff= (a - b)/sizeof(type);
    
    return (ptrdiff_t)a-b;
}

Computing pointer differences with a zero-byte structure thus involves division by zero, undefined behaviour in C. The C++ behaviour may seem strange at first, but it is to keep perfectly reasonable pointer arithmetic from failing for some types. As magicmanzach points out below, this is especially important because C++'s template system allows generic programming. It often makes sense to write generic code using pointer arithmetic, but it is safe to do so only for types with positive sizes. Rather than require template writers (or template users) to explicitly check for empty types, the designers of C++ instead declared that all types have positive size.

Such things happen when you try to cross a glorified macro assembler with Simula, in the meanwhile adding a (completely optional thanks to casts) type system almost as strict as that of Modula-2---so strict that many C programmers moving to C++ learn to ignore type-safety warnings from the compiler, especially when const is involved. Not to mention template processing that rivals PHP in power and unlambda in comprehensibility.

Log in or registerto write something here or to contact authors.