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.