22

I am trying to understand program below, but it's not clear to me.

    #include<stdio.h>
    int main()
    {
        int a[]={1,2,3,4,5,6,9};
        printf("sizeof array is %d\n",sizeof(a));
        printf("size of array using logic is %d\n",((&a)[1]-a));
        printf("value of (&a)[1] is %p \n",(&a)[1]);
        printf("value of a is %p \n",a);
        printf("address of a[0] is %p\n",&a[0]);
        printf("address of a[1] is %p\n",&a[1]);
        printf("address of a[2] is %p\n",&a[2]);
        printf("address of a[3] is %p\n",&a[3]);
        printf("address of a[4] is %p\n",&a[4]);
        printf("address of a[5] is %p\n",&a[5]);
        printf("address of a[6] is %p\n",&a[6]);
    }

Above code output is :

    sizeof array is 28
    size of array using logic is 7
    value of (&a)[1] is 0x7ffc4888e78c 
    value of a is 0x7ffc4888e770 
    address of a[0] is 0x7ffc4888e770
    address of a[1] is 0x7ffc4888e774
    address of a[2] is 0x7ffc4888e778
    address of a[3] is 0x7ffc4888e77c
    address of a[4] is 0x7ffc4888e780
    address of a[5] is 0x7ffc4888e784
    address of a[6] is 0x7ffc4888e788

It's not clear to me why ((&a)[1]-a)) on second print statement is returning 7; it should be 0x7ffc4888e78c - 0x7ffc4888e770 which is 0x1c i.e 28 total size of array.

For reference I also tried printing (&a)[1] and a values which you can see in code. I tried also debugging.

7
  • 3
    (&a)[1]-a is pointer difference. Commented Jun 2, 2016 at 5:07
  • 2
    enable compiler warnings and note that you have %zu for size_t. Commented Jun 2, 2016 at 5:22
  • @haccks why pointer difference is 7 not something-else? Commented Jun 2, 2016 at 5:22
  • 3
    @Mohan because no of array elements are 7 Commented Jun 2, 2016 at 5:24
  • 2
    @harish; Pointer difference equals the integer expression as a result, not an address. Commented Jun 2, 2016 at 7:48

6 Answers 6

15

If you cast (&a)[1] and a to long before the calculation, then you will get your expected result. As haccks commented, you're currently calculating the pointer difference.

// These two sizes will be the same
printf("sizeof array is %ld\n",sizeof(a));
printf("size of array using logic is %ld\n",((long)(&a)[1]-(long)a));

Explaining the Math

What's happening in this case, is &a is considered to be type int(*)[7].

Then, you reference (&a)[1], which translates to *((&a)+1). In English, this means "give me the point in memory 1 after the beginning of a." As &a happens to be type int(*)[7], that point is at the end of the array.

When you subtract a, the pointer to the beginning of the array, you are performing pointer arithmetic and take a base the size of an int (because a is an int array). So the expression ((&a)[1]-a) is counting how many int are between (&a)[1] and a.

An overview on pointer arithmetic can be found here.

Sign up to request clarification or add additional context in comments.

16 Comments

Ya thanks,but without type casting I am getting 7 which is still not clear to me.All say that it is same as sizeof(a)/sozeof(a[0]).But telling it is similar to this is Ok but while debugging how can I clearly understand this difference.
This is a part of pointer math. You can read more about it here. I've updated the answer with a more in depth description of what's happening.
@2501 Why isn't &a type int*[7]? My compiler is telling me that is correct.
@2501 Thanks, I was messing up my notation. I did mean int(*)[7]. Updated the answer accordingly.
|
5

So pointers are not integers. Sure, you can convert them to integers by casting them to an integer type, or add integers to them to slide them around. But they are not integers.

Pointers are like mathematical vectors over the integers, if you have done any linear algebra.

p1-p2 is the distance between p1 and p2, the integer required to add to p2 to reach p1.

When you add an integer to a pointer, you have to pay attention to the type of the pointer. If the pointer is to an object of size 4, each time you add 1 to a pointer its numerical address increases by 4, not 1.

The same thing is true when you subtract two pointers.

The key part here is that the numerical value of the address in memory matters, but the type matters just as much to understand what happens.

The second strange thing going on here is that arrays decay into pointers to their first element at the drop of a hat. They, however, are not pointers to their first element, they just convert into them very easily.

So when we do this:

(&a)[1]

we are taking the address of a. The address of a is a pointer of type int(*)[7]. It is a pointer to an array, not a pointer to the first element of the array. The difference is in the type of the pointer. And that 7 is important.

We then use [] on the pointer. If you have a pointer or array p and a value v, p[v] is defined to be *(p+v). This leads to humor if you do v[p], but that isn't important.

Let pa represent (&a). Then pa[1] is going to be *(pa + 1).

Now, pa is a pointer-to-an-array (not a pointer-to-the-first-element of the array). So +1 adds the full size of the array (sizeof(int)*7) to the numeric value.

So pa+1 is a pointer to one-past-the-end of a, and is of type pointer-to-array.

We then dereference, and get the non-existent array of size 7 right after the end of the array a.

Then we subtract a.

(&a)[1]-a

This is where pointer decay kicks in. There is no - operation on arrays, but there is a - operation on pointers. So the C language helpfully decays each of these arrays into pointers to their first element.

The pointer to the first element of a is &a[0].

The pointer to the first element of the array of size 7 immediately after the end of a is ... &a[7].

Both of these pointers are of type int*. When you subtract two int*s, you get their numeric pointer value, divided by sizeof(int). In this case, this is easy -- 7.

This might be easier if we looked at this:

(&a)[1]-(&a)[0]

or

*(&a+1)-*(&a+0)

&a is a pointer to the array a of type "pointer to array of size 7". We add 1 to it, getting the pointer to the array afterwards in one case, and zero in the other case.

We then go down back to being arrays, and subtract. Subtraction triggers decay to pointer-to-first-element, so we get a pointer to the element right after the end of a, and a pointer to the first element of a.

&a[7]-&a[0]

which is

&*(a+7)-&*(a+0)

Now &* does nothing to things that are already pointers (which they are at that point), so:

(a+7)-(a+0)

The question then becomes, how much do you have to add to a+0 to reach a+7. The answer, not surprisingly, is 7:

(a+7) = (a+0)+7

and that is what is displayed.

1 Comment

Thanks for clear explanation.I figured out by checking objdump for this code and also with type cast code.
5

(&a)[1] is the address of the memory location past the array a, i.e. 0x7ffc4888e788. (&a)[1] is of type int *. After conversion, a will be of type int *. It is equivalent to (&a)[0].

Standard says that:

C11-§6.5.6/9:

When two pointers are subtracted, both shall point to elements of the same array object, or one past the last element of the array object; the result is the difference of the subscripts of the two array elements.

The difference (&a)[1]-a gives the number of elements in array a. Note that a in this expression is the address of array element a[0], after decay, and this address is equivalent to the address of array a, though &a and a[0] have different type.

You can think of this difference as (&a)[1]-(&a)[0] or &a[7] - &a[0].

sizeof(a) gives the memory size allocated for array a. sizeof(a)/sizeof(a[0]) will give the number of elements of array a.

5 Comments

is there any theoretical explanation for why (&a)[1] gives address of the memory location past the array and (&a)[1]-a gives the number of elements in array a and it's return value change when casted to long as @Carter mentioned?
If you could put a small note on the difference between (&a)[x] and &a[x], that will be helpful for the future audience :-)
@CherubimAnand Yes, there is! I've updated my answer with a more in depth explanation, but it basically boils down to pointer arithmetic. You can read more about it here.
@CherubimAnand; After casting it no longer be pointer difference, rather it would be integer difference. Integer difference of 0 - 4 will give 4 while pointer difference will give 1 (assuming long size 4).
(&a)[0] and &a are not the same thing. (&a)[1]-&a would be a syntax error, while ``(&a)[1]-(&a)[0]` is the same as (&a)[1]-a which is the same as (&a)[1]-&(a[0]). Types matter.
2

You operates but int* pointer. All arithmetic operations on it using 4 bytes (sizeof(int) to be precise) as unit. Difference betwen two pointers expressed into this units. ((&a)[1]-a)) is equals as sizeof(a)/sizeof(a[0]). For calc array size into bytes you need to cast pointer to integer value, unsigned int:

  printf("size of array using logic is %d\n",((int)((&a)[1])-(int)a));

5 Comments

preferably cast to long instead of int.
Yes, I made a mistake.
Ya thanks,but without type casting I am getting 7 which is still not clear to me.All say that it is same as sizeof(a)/sozeof(a[0]).But telling it is similar to this is Ok but while debugging how can I clearly understand this difference.
@sjsam prefer cast to uintptr_t
@M.M. Ah much better :-)
2

If you want to get number of bytes, without using sizeof operator, then rather than casting to long data type*, I think that more idiomatic and safe way is to cast both pointers into char *:

printf("Size of array using pointer arithmethic is %td.\n", (char*)(&a)[1] - (char*)a);

Result:

Size of array using pointer arithmethic is 28.

Note that %td format specifier is suited for datatype ptrdiff_t (defined in <stddef.h>), that is how pointer difference is represented.


*) There are dedicated data types intptr_t and uintptr_t for representing object pointers as integers if you really need that.

Comments

0

First thanks to all and special thanks to Yakk for giving me such great analysis on simple pointer arthamatics.I at last figured out why it is happening so as @Yakk explained in detail that cleared me to much extent but still had some doubt on that,so I started changing code and tryed to verify pointer arthematics. One short answer is if &a[0] is used it refers to first element in array address. If a or &a are used they refer to base address of complete array of size 7. Now to go clear ,we used (&a)[0] which point to base address of array of size 7 when incremented to 1 it goes to one-past-the-end of array a. As explained by --Yakk as below: This might be easier if we looked at this:

(&a)[1]-(&a)[0]

or

(&a+1)-(&a+0)

&a is a pointer to the array a of type "pointer to array of size 7". We add 1 to it, getting the pointer to the array afterwards in one case, and zero in the other case.

We then go down back to being arrays, and subtract. Subtraction triggers decay to pointer-to-first-element, so we get a pointer to the element right after the end of a, and a pointer to the first element of a.

&a[7]-&a[0]

which is

&(a+7)-&(a+0)

Now &* does nothing to things that are already pointers (which they are at that point), so:

(a+7)-(a+0)

The question then becomes, how much do you have to add to a+0 to reach a+7. The answer, not surprisingly, is 7:

(a+7) = (a+0)+7

and that is what is displayed.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.