One of the odder things about C is that one is not always, well, one.

For instance, consider this output:

$ ./woisno
x0 = 5, x0 + 1 = 6
x1 = 5, x1 + 1 = 7
x2 = 5, x2 + 1 = 13
x3 = 5, x3 + 1 = 165	

It's not a trick of fake output: here's the C code that generated that:

  printf("x0 = %lu, x0 + 1 = %lu\n",(uint64_t)x0,(uint64_t)(x0+1));
  printf("x1 = %lu, x1 + 1 = %lu\n",(uint64_t)x1,(uint64_t)(x1+1));
  printf("x2 = %lu, x2 + 1 = %lu\n",(uint64_t)x2,(uint64_t)(x2+1));
  printf("x3 = %lu, x3 + 1 = %lu\n",(uint64_t)x3,(uint64_t)(x3+1));

So what *is* going on?

It's a matter of types:

  struct x3_x3
  {
    char *arr[20];
  };

  uint64_t x0 = 5;
  uint16_t *x1 = (uint16_t *)5;
  uint64_t *x2 = (uint64_t *)5;
  struct x3_x3 *x3 = (struct x3_x3 *)5;

What is happening is the unusual rule that adding to a pointer type in C is actually done on the size of whatever is being pointed at. Kernighan and Richie's classic "The C Programming Language" reveals this, appropriately enough, in the chapter "Pointers and Arrays." Here's the key sentence: 'The meaning of "adding 1 to a pointer," and by extension, all pointer arithmetic, is that pa + 1 points to the next object, and pa + i points to the i-th object beyond pa.'

They continue with "[t]he correspondence between indexing and pointer arithmetic is very close. By definition, the value of a variable or expression of type array is the address of element zero of the array. Thus after the assignment pa = &a[0]; pa and a have identical values."

(The code for above)