11
int main()
{
    char arr[5][7][6];
    char (*p)[5][7][6] = &arr;
    printf("%d\n", (&arr + 1) - &arr);
    printf("%d\n", (char *)(&arr + 1) - (char *)&arr);
    printf("%d\n", (unsigned)(arr + 1) - (unsigned)arr);
    printf("%d\n", (unsigned)(p + 1) - (unsigned)p);

    return 0;
}

When I run the above code I get following output:

 1
 210 
 42
 210

Why is the output not 1 in every case?

Alex
  • 1,178
  • 3
  • 9
  • 24
  • 4
    Hint: `6 * 7 = 42` , `5 * 7 * 6 = 210` –  Jul 16 '13 at 13:58
  • @Armin Can you please explain? – Alex Jul 16 '13 at 14:02
  • 1
    Another hint - you're not converting the address into a different type until *after* you've already done the arithmetic on the pointers, which is done assuming the original type, not the final type... – twalberg Jul 16 '13 at 14:21
  • @twalberg for what example would that be true, surely not for the last three of them. –  Jul 16 '13 at 14:28
  • @Armin This is true of all of the last three examples above, due to operator precedence rules -- `(char *)(&arr + 1)` for example, knows that `arr` is a `char[5][7][6]`, which means that `&arr + 1` is `arr + 5*7*6` or `arr + 210`, which is then cast to a `char *` - `(char *)(arr) + 1` would be the way to convert `arr` to a `char *` before the arithmetic is done. – twalberg Jul 16 '13 at 14:34
  • @twalberg You're right. I was looking at something else and completely unimportant. –  Jul 16 '13 at 14:38

3 Answers3

6

Well, if I wanted to split hair: in first place, the code invokes undefined behavior all over the place, throughout the printf() statements. The difference of two pointers has type ptrdiff_t, and for that, the correct conversion specifier is %td, not %d.

The rest is only speculation. Let's suppose your system is reasonable, and numerically the pointer value &arr is always the same, whatever type it be converted to.

Now, (&arr + 1) - &arr is 1, of course, according to the rules of pointer arithmetic. (The actual difference between the two pointers is 210 * sizeof(int) bytes, but this is not school maths but pointer arithmetic, that's why the result is given in units of size sizeof(T), where T is the base type of the pointer.)

Then (char *)(&arr + 1) - (char *)&arr casts the pointers to char *, and since the size of char is 1, this will print the difference in bytes; you're effectively tricking/abusing pointer arithmetic here.

Furthermore: printf("%d\n", (unsigned)(arr + 1) - (unsigned)arr) is subtracting two pointers of type int (*)[7][6]. That's what arr decays into. Of course, 7 * 6 = 42, so the size difference between arr + 1 and arr is 42 elements.

p, however, is not a pointer to the first element of the array, but it's a pointer to the array itself. Its type is correctly denoted as int (*)[5][6][7]. Now if you print the difference using that type, but you don't let the compiler do the division by fooling it into that the pointers are just unsigned, then you will get 5 * 6 * 7, which is 210.

  • 1
    Whoever downvoted, leave a comment. I don't see any reason for a downvote. –  Jul 16 '13 at 14:08
  • 3
    The answer is poorly written and does not explain the types involved. Then answer is written in such a way as to make true statements and to show off but not to teach concepts to somebody who does not understand them. Also, converting to `char *` and subtracting is not tricking or abusing pointer arithmetic; it is fully defined by the C standard. – Eric Postpischil Jul 16 '13 at 14:20
5

In (&arr + 1) - &arr:

&arr is the address of an array of 5 arrays of 7 arrays of 6 char. Adding one produces the address of where the next array of 5 arrays of 7 arrays of 6 char would be, if we had an array of those objects instead of just one. Subtracting the original address, &arr, produces the difference between the two addresses. Per the C standard, this difference is expressed as the number of elements between the two addresses, where the element type is the type of object being pointed to. Since that type is an array of 5 arrays of 7 arrays of 6 char, the distance between the two addresses is one element. In other words, the distance from &arr to (&arr + 1) is one array of 5 arrays of 7 arrays of 6 char.

In (char *)(&arr + 1) - (char *)&arr:

&arr + 1 is again a pointer to where the next array of 5 arrays of 7 arrays of 6 char would be. When it is converted to a char *, the result is a pointer to what would be the first byte of that next array. Similarlly, (char *)&arr is a pointer to the first byte of the first array. Then subtracting the two pointers yields the difference between them in elements. Since these pointers are pointer to char, the difference is produced as a number of char. So the difference is the number of bytes in an array of 5 arrays of 7 arrays of 6 char, which is 5•7•6 char, or 210 char.

In (unsigned)(arr + 1) - (unsigned)arr:

Since arr is not used with & (or sizeof or other exceptional cases), it is automatically converted from an array of 5 arrays of 7 arrays of 6 char to a pointer to the first element. Thus, it is a pointer to an array of 7 arrays of 6 char. Then arr + 1 is a pointer to the next array of 7 arrays of 6 char. When this address is converted to unsigned, the result, in the C implementation you are using, is in effect the memory address of the object. (This is common but is not guaranteed by the C standard and certainly breaks when addresses are 64 bits but unsigned is 32 bits.) Similarly, (unsigned)arr is the address of the first object. When the addresses are subtracted, the result is the distance between them in bytes. So the difference is the number of bytes in an array of 7 arrays of 6 char, which is 7•6 bytes, or 42 bytes. Note the key difference in this case: &arr is a pointer to an array of 5 arrays of 7 arrays of 6 char, but arr is a pointer to an array of 7 arrays of 6 char.

In (unsigned)(p + 1) - (unsigned)p:

p is a pointer to an array of 5 arrays of 7 arrays of 6 char. Then p+1 is a pointer to where the next array would be. Converting to unsigned acts as described above. Subtracting yields the difference it bytes, so it is the size of an array of 5 arrays of 7 arrays of 6 char, so it is again 210 bytes.

As an aside:

The type of (&arr + 1) - &arr is ptrdiff_t and should be printed with %td.

The type of (char *)(&arr + 1) - (char *)&arr is ptrdiff_t and should be printed with %td.

The type of (unsigned)(arr + 1) - (unsigned)arr is unsigned int and should be printed with %u.

The type of (unsigned)(p + 1) - (unsigned)p is unsigned int and should be printed with %u.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • @GrijeshChauhan: The additions, subtractions, and conversions to `char *` in this code are defined. The conversions to `unsigned` are not fully defined but are well behaved in typical C implementations provided the resulting addresses are within bounds of the `unsigned` type. The use of incorrect format specifiers is not defined. – Eric Postpischil Jul 16 '13 at 20:19
  • Thanks!! I got it now, I read that section of your answer again (i voted 2nd). but my comment-voting limit crossed. Thanks – Grijesh Chauhan Jul 16 '13 at 20:34
5

Note &arr is complete 3-dimensional char array's address, whereas arr points to first element that is 2-dimensional char array. Something like below in diagram:

 0xbf8ce2c6
+------------------+     ◄-- arr  =  0xbf8ce2c6  
|    0xbf8ce2f0    |  
|   +------------------+     ◄-- arr + 1 = 0xbf8ce2f0
|   |   0xbf8ce31a |   |
|   |   +------------------+      ◄-- arr + 2 = 0xbf8ce31a 
|   |   0xbf8ce344 |   |   |
|   |   |   +------------------+      ◄-- arr + 3 = 0xbf8ce344
|   |   0xbf8ce36e |   |   |   |
|   |   |   |  +------------------+      ◄-- arr + 4 = 0xbf8ce36e
|   |   |   |  |   |   |   |   |  |
+---|---|---|--|---+   |   |   |  |  Each are 7*6, 2-Dimensional 
    |   |   |  |       |   |   |  |  Consists Of 42 bytes 
    +---|---|--|-------+   |   |  |  
        |   |  |           |   |  |
        +---|--|-----------+   |  |
            |  |               |  |
            +--|---------------+  |
               |                  |
               +------------------+

 The diagram show: 
 1. How a 3-dimensional can be interpreted as series of 2-dimensional arrays
 2. Here (arr + i) points to a 2-D array 
 3. Notice difference between: (arr + i + 1) - (arr + i) = 0x2a = 42, where i = [0, 4]

Type of &arr is char(*)[5][7][6] that is address of char 3D-array of dimension [5][7][6]. Value-wise difference between &arr and &arr + 1 is 5 * 7 * 6 * sizeof(char) = 210.
Because size of char[5][7][6] is 5 * 7 * 6 * sizeof(char).
In your code &arr points to 3-D array and &arry + 1 next 3-D array (that doesn't exist in our code).

Check this working code at codepade:

int main()
{
    char arr[5][7][6];
    printf(" &arr  : %p", &arr);
    printf(" &arr+1: %p", &arr + 1);

    return 0;
}

Output:

 &arr  : 0xbf5dd7de
 &arr+1: 0xbf5dd8b0

Difference between (&arr + 1) - (&arr) = 0xbf5dd8b0 - 0xbf5dd7de = 0xd2 = 210.

In your second printf:

printf("%d\n", (char *)(&arr + 1) - (char *)&arr);

You typecasts addresses of type char(*)[5][7][6] to plain (char*), and because sizeof char[5][7][6] is 210 both addresses are 210 far. (remember sizeof(char) == 1). This is the reason outputs: 210

Now as I said in first statement, arr is address of first element that is a two dimensional array of chars. Type of arr is char(*)[7][6]. Now one element (two-dimensional array of size is 6 * 7 * sizeof(char) = 42).
(Note: you can think a 3-D array as one-d array where each element is a 2-d array).

In your third printf:

printf("%d\n", (unsigned)(arr + 1) - (unsigned)arr);

You typecasts to unsigned value (but not to an address/pointer type). The difference between arr + 1 and arr is 42 * sizeof(char) = 42 (that is equals to size of char[7][6]). So the printf statement outputs: 42.

Note: You should read sizeof (int) == sizeof (void*)?, because you are typecasting address to value. and this conversion is not fully defined. (my explanation is wrt your output and the output I have given).

For further clarification check below working code at codepade:

int main()
{
    char arr[5][7][6];
    printf(" arr  : %p\n", arr);
    printf(" arr+1: %p", arr + 1);

    return 0;
}

Output is:

 arr  : 0xbf48367e
 arr+1: 0xbf4836a8

Take difference between (arr + 1) - (arr) = 0xbf4836a8 - 0xbf48367e = 0x2a = 42.

Last printf:

 printf("%d\n", (unsigned)(p + 1) - (unsigned)p);

Just take difference between &arr+1 and &arr = 210 (similar to second printf) because p is pointer to 3-D char array (=&arr). And you are typecasting it to value type(not pointer type).

Additionally, (Just adding for understanding purpose, I guess reader will find it helpful),

Lets we learn one more difference between arr and &arr using sizeof operator that will help your to understand concept more deeper. For this first read: sizeof Operator

When you apply the sizeof operator to an array identifier, the result is the size of the entire array rather than the size of the pointer represented by the array identifier.

Check this working code at codepade:

int main()
{
    char arr[5][7][6];
    printf(" Sizeof(&arr)  : %lu and value &arr: %p\n", sizeof(&arr), &arr);
    printf(" Sizeof(arr)   : %lu and value arr : %p\n", sizeof(arr), arr);
    printf(" Sizeof(arr[0]): %lu and value a[0]: %p\n",sizeof(arr[0]), arr[0]);
    return 0;
}

Its output:

Sizeof(&arr)  : 4 and value &arr: 0xbf4d9eda
Sizeof(arr)   : 210 and value arr : 0xbf4d9eda
Sizeof(arr[0]): 42 and value a[0]: 0xbf4d9eda
  • Here &arr is just an address, and in the system address is of four-bytes and this is address of complete 3-dimensional char array.

  • arr is name of 3-dimensional array, and sizeof operator gives total size of array that is 210 = 5 * 7 * 6 * sizeof(char).

As I shown in my diagram arr points to first elements that is an 2-dimensional array. So because arr = (arr + 0). Now using * Dereference operator at (arr + 0) gives value at address so *(arr + 0) = arr[0].

  • Notice sizeof(arr[0]) gives 42 = 7 * 6 * sizeof(char). And this proofs conceptually a 3-dimensional array is noting but array of 2-dimensional array.

Because above in my answer at many time I written like: "size of char[5][7][6] is 5 * 7 * 6 * sizeof(char)." so I am adding an interesting code below @codepade:

int main(){
 printf(" Char         : %lu \n", sizeof(char));
 printf(" Char[5]      : %lu \n", sizeof(char[6]));
 printf(" Char[5][7]   : %lu \n", sizeof(char[7][6]));
 printf(" Char[5][7][6]: %lu \n", sizeof(char[5][7][6]));

 return 1;
}

Output:

 Char         : 1 
 Char[5]      : 6 
 Char[5][7]   : 42 
 Char[5][7][6]: 210 
Community
  • 1
  • 1
Grijesh Chauhan
  • 57,103
  • 20
  • 141
  • 208
  • Check this [diagram](http://alibad.files.wordpress.com/2010/05/array3d-jagged.jpg?w=369&h=590) – Grijesh Chauhan Jul 16 '13 at 17:25
  • @Alex try your code again with each typecase this time: `(char (*)[5][7][6])`. for example: `printf("%d\n", ((char (*)[5][7][6])(arr + 1) - ((char (*)[5][7][6])arr);` and tell the reason of output. – Grijesh Chauhan Jul 17 '13 at 02:32