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.

user694733
  • 13,861
  • 1
  • 40
  • 62
Harish
  • 344
  • 1
  • 12

6 Answers6

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.

Carter
  • 2,803
  • 1
  • 13
  • 21
  • 2
    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. – Harish Jun 02 '16 at 05:46
  • This is a part of pointer math. You can read more about it [here](https://www.cs.umd.edu/class/sum2003/cmsc311/Notes/BitOp/pointer.html). I've updated the answer with a more in depth description of what's happening. – Carter Jun 02 '16 at 05:58
  • is base size int or int[7]? – sjsam Jun 02 '16 at 06:16
  • *As a happens to be type int*[7]* That is not the type of a and neither is &a. – 2501 Jun 02 '16 at 06:17
  • @sjsam The base size is `int` – Carter Jun 02 '16 at 06:19
  • 1
    @2501 Why isn't `&a` type `int*[7]`? My compiler is telling me that is correct. – Carter Jun 02 '16 at 06:20
  • and everything makes sense if `a` is considered as `int*[7]` type. good explanation – Cherubim Jun 02 '16 at 06:28
  • @Carter I don't know why your compiler is telling you that, but neither `a` or `&a` have the type `int*[7]`, which is actually an array of 7 pointers to int. – 2501 Jun 02 '16 at 06:33
  • 1
    @2501 Thanks, I was messing up my notation. I did mean `int(*)[7]`. Updated the answer accordingly. – Carter Jun 02 '16 at 06:39
  • 10
    don't cast to `long`, [use `ptrdiff_t` and print with `%td` or `off_t` and print with `%jd`](http://stackoverflow.com/q/586928/995714) – phuclv Jun 02 '16 at 06:42
  • @Carter : Overall great explnation. Regarding the base, check [\[ this \]](http://pastebin.com/raw/y2qUNRZG) if time permits. – sjsam Jun 02 '16 at 07:17
  • @LưuVĩnhPhúc Don't cast the pointers to integers at all! There's no particular reason that `(intptr_t)a - (intptr_t)b` should make any sense. Instead, use `(char*)a - (char*)b`. – Tavian Barnes Jun 02 '16 at 16:20
  • @TavianBarnes doesn't `(char*)a - (char*)b` return a [`ptrdiff_t`](http://en.cppreference.com/w/c/types/ptrdiff_t)? And when did I say that you should cast to an integer like `intptr_t`? – phuclv Jun 02 '16 at 16:26
  • @LưuVĩnhPhúc This answer casts pointers to `long`s. You suggested to use `ptrdiff_t` instead of `long`, which is sill casting a pointer to an integer. – Tavian Barnes Jun 02 '16 at 16:27
  • @TavianBarnes I suggest not casting **at all** and print that `ptrdiff_t` out. Please read again – phuclv Jun 02 '16 at 16:35
  • Ah okay, I did not see the other interpretation of "don't cast to `long`, use `ptrdiff_t`" until now. But if you remove the cast, the result will be 7 instead of 28. – Tavian Barnes Jun 02 '16 at 16:38
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.

haccks
  • 97,141
  • 23
  • 153
  • 244
  • 1
    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? – Cherubim Jun 02 '16 at 05:44
  • 2
    If you could put a small note on the difference between (&a)[x] and &a[x], that will be helpful for the future audience :-) – sjsam Jun 02 '16 at 05:49
  • @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](https://www.cs.umd.edu/class/sum2003/cmsc311/Notes/BitOp/pointer.html). – Carter Jun 02 '16 at 06:05
  • 2
    @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`). – haccks Jun 02 '16 at 06:15
  • `(&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. – Yakk - Adam Nevraumont Jun 02 '16 at 15:48
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.

Yakk - Adam Nevraumont
  • 235,777
  • 25
  • 285
  • 465
  • Thanks for clear explanation.I figured out by checking objdump for this code and also with type cast code. – Harish Jun 03 '16 at 05:06
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));
ameyCU
  • 15,956
  • 2
  • 22
  • 40
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.

Grzegorz Szpetkowski
  • 35,042
  • 4
  • 82
  • 127
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.

Harish
  • 344
  • 1
  • 12