17

I was searching for a way to find the size of an array in C without using sizeof and I found the following code:

int main ()
{
    int arr[100];
    printf ("%d\n", (&arr)[1] - arr);
    return 0;
}

Can anyone please explain to me how is it working?

S.S. Anne
  • 15,171
  • 8
  • 38
  • 76
poorvank
  • 7,524
  • 19
  • 60
  • 102
  • 4
    There is never a reason why you can't use `sizeof`. – Lundin Apr 15 '13 at 15:31
  • It seems that the array, which has 100 elements is actually treated as item 0 of an (unnamed) array. – enhzflep Apr 15 '13 at 15:32
  • Nice trick, it's also standard compliant. – effeffe Apr 15 '13 at 15:45
  • @effeffe: maybe, but see Daniel's comments below. I don't think it is standard compliant, although I can't think of a good reason for it not to work in practice. The subtraction, I mean -- the use of `%d` is certainly not strictly conforming and in fact would fail on a fairly normal-looking big-endian implementation with a 32 bit `int` and a 64 bit `ptrdiff_t`. – Steve Jessop Apr 15 '13 at 16:09
  • @SteveJessop actually you're right, it could be not standard compliant, let's follow that discussion there. However, I agree that it should work, but the standard can't let us dereference a pointer that doesn't actually point to an object or a part of it, that makes sense. – effeffe Apr 15 '13 at 18:01
  • @SteveJessop The `%d` generates a warning on my compiler (clang). Should be `%ld` – JeremyP Mar 11 '15 at 11:22

6 Answers6

25

&arr is a pointer to an array of 100 ints.

The [1] means "add the size of the thing that is pointed to", which is an array of 100 ints.

So the difference between (&arr)[1] and arr is 100 ints.

(Note that this trick will only work in places where sizeof would have worked anyway.)

RichieHindle
  • 272,464
  • 47
  • 358
  • 399
  • 9
    `[1]` doesn't mean "add the size of the thing that is pointed to". It means "add the size of the thing that is pointed to and then dereference the resultant pointer". – Alexey Frunze Apr 15 '13 at 15:54
13

&arr gives you a pointer to the array. (&arr)[1] is equivalent to *(&arr + 1). &arr + 1 gives you a pointer to the array of 100 ints that follows arr. Dereferencing it with * gives you that array that follows. Since this array is used in an additive expression (-), it decays to the pointer to its first element. The same happens to arr in the expression. So you subtract to pointers, one pointing to the non-existent element right after arr and the other pointing to the first element of arr. This gives you 100.

But it's not working. %d is used for int. Pointer difference returns you ptrdiff_t and not int. You need to use %td for ptrdiff_t. If you lie to printf() about the types of the parameters you're passing to it, you get well-deserved undefined behavior.

EDIT: (&arr)[1] may cause undefined behavior. It's not entirely clear. See the comments below, if interested.

Alexey Frunze
  • 61,140
  • 12
  • 83
  • 180
  • 3
    Isn't `(&arr)[1] - arr` also undefined behaviour? – Daniel Fischer Apr 15 '13 at 15:45
  • 2
    @DanielFischer C99: Additive operators: `For the purposes of these operators, a pointer to an object that is not an element of an array behaves the same as a pointer to the first element of an array of length one with the type of the object as its element type.` So, it looks like this is OK. I mean the `[1]` part. And the rest is as usual. – Alexey Frunze Apr 15 '13 at 15:50
  • 1
    But the last sentence in paragraph 8 of that is "If the result points one past the last element of the array object, it shall not be used as the operand of a unary `*` operator that is evaluated." And `(&arr)[1]` evaluates the `*` in `*(&arr + 1)`, if I understand correctly. It's okay if you take the address, of `(&arr)[1]`, or apply `sizeof` to it, that doesn't evaluate the `*`, but with `-`? – Daniel Fischer Apr 15 '13 at 15:55
  • @DanielFischer: The standard says that `&*` is a no-op even if dereferencing would be UB (6.5.3.2/3 in C99). In the same spirit we might expect that to apply with decay-to-pointer in the place of `&`, but I think you're correct that this is not guaranteed in the standard. – Steve Jessop Apr 15 '13 at 16:03
  • @DanielFischer 6.5.3.2c4 of C99 has footnote 83 that says that `*&E` is equivalent to `E` and it reiterates the same for `&*E` as mentioned by Steve. – Alexey Frunze Apr 15 '13 at 16:05
  • @AlexeyFrunze: my point was that although it says this for `&`, it *doesn't* say it for array decay. They are not the same thing, even though they both involve finding an address. So unless I've missed something Daniel is correct. This is UB in C99, although we might argue that's a defect in the standard. – Steve Jessop Apr 15 '13 at 16:06
  • 3
    @AlexeyFrunze But we don't have `*(&E)`, we have `*(&E + 1)`. – Daniel Fischer Apr 15 '13 at 16:06
  • @DanielFischer Is there a difference between `&arr + 1` and `&imaginary_array_of_100_ints_following_arr`? – Alexey Frunze Apr 15 '13 at 16:14
  • Would be UB to write `(int *) (&a + 1) - a`, losing some type generality but not dereferencing a pointer to a non-existent object? – effeffe Apr 15 '13 at 16:52
  • @effeffe `(int*)` is unnecessary, you get this type anyway, no need to cast to it one more time. – Alexey Frunze Apr 15 '13 at 16:58
  • @AlexeyFrunze 6.5.3.2 (1) says "The operand of the unary `&` operator shall be [...] or an lvalue that designates an object [...]". Does `imaginary_array...` designate an object? Anyway, evaluating `*(&arr+1)` is UB, the question is whether it is evaluated here or the array-to-pointer conversion annihilates the evaluation. I don't know whether it does, hence my question. – Daniel Fischer Apr 15 '13 at 17:30
  • @DanielFischer I don't know either what to make of it. – Alexey Frunze Apr 15 '13 at 17:32
  • @AlexeyFrunze `&a + 1` has type `int (*)[100]`; the conversion does not happen implicitly, the code doesn't even compile without it. – effeffe Apr 15 '13 at 17:50
  • @userq `(&arr)[1]` is an array of 100 ints immediately following `arr`. In `(&arr)[1] - arr` both arrays convert to pointers to their first elements. And those are 100 elements apart. – Alexey Frunze Sep 15 '15 at 18:52
  • @DanielFischer [It seems you are right in telling that `(&arr)[1]` invokes UB](http://stackoverflow.com/a/32539578/3049655) – Spikatrix Oct 05 '15 at 11:13
1

Generally (as per visual studio), for an array &arr is same as arr ,which return the starting base address of our function.

(&arr)[0] is nothing but &arr or arr

ex: it will return some address : 1638116

Now, (&arr)[1] means we are started accessing the array out of bounce means next array or next segment of the size of present array(100 ahead).

ex: it will return some address : 1638216

Now, subtracting (&arr)[1] - (&arr)[0]=100

Spikatrix
  • 20,225
  • 7
  • 37
  • 83
  • "***`(&arr)[0]` is nothing but `&arr` or `arr`***" -- `arr` and `&arr` have different types when though both point to the same location. The former is of type `int*` while the latter is of type `int(*)[100]`. – Spikatrix Oct 05 '15 at 11:18
0

&variable gives location of the variable (call it as P)
&variable + 1 gives address of the location next to the variable. (call it as N)

(char*)N-(char*)P gives how many characters are there between N and P. Since each character is 1 byte sized, so the above result gives the number of bytes P and N. (which equals to the size of array in bytes).

Similarly, (char*) (a+1)-(char*)a; gives size of each element of the array in bytes.

So the number of elements in the array = (size of array in bytes)/(size of each element in the array in bytes)

#include<stdio.h>

int main()
{
    int a[100];
    int b = ((char*)(&a+1)-(char*)(&a));
    int c = (char*) (a+1)-(char*)a;
    b = b/c;
    printf("The size of array should be %d",b);
    return 0;

}
Spikatrix
  • 20,225
  • 7
  • 37
  • 83
Neo
  • 1
0

int arry[6]={1,2,3,4,5,6} //lets array elements be 6, so... size in byte = (char*)(arry+6)-(char *)(arry)=24;

  • 1
    OP asked to explain how his example is working. You gave another example. That's not an answer to OP's question. – Adrian W Jun 29 '18 at 20:03
-2
int main ()
{
  int arr[100];
  printf ("%d\n", ((char*)(&arr+1) - (char*)(&arr))/((char*) (arr+1) -(char*) (arr)));
  return 0;
}
m4n0
  • 29,823
  • 27
  • 76
  • 89
  • 2
    While this code may answer the question, providing additional context regarding why and/or how this code answers the question improves its long-term value. – JAL Oct 04 '15 at 23:05
  • I'll be happy to upvote your answer if you add some explanation about how your code works. BTW, you should use `%ld`, not `%d`. – Spikatrix Oct 05 '15 at 11:43