Find size of array without using sizeof
Asked Answered
J

6

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?

Joscelin answered 15/4, 2013 at 15:25 Comment(6)
There is never a reason why you can't use sizeof.Fenestrated
It seems that the array, which has 100 elements is actually treated as item 0 of an (unnamed) array.Unfit
Nice trick, it's also standard compliant.Candelaria
@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.Baptize
@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.Candelaria
@SteveJessop The %d generates a warning on my compiler (clang). Should be %ldBrickbat
F
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.)

Festoonery answered 15/4, 2013 at 15:27 Comment(1)
[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".Vitriol
V
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.

Vitriol answered 15/4, 2013 at 15:34 Comment(15)
Isn't (&arr)[1] - arr also undefined behaviour?Thermolysis
@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.Vitriol
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 -?Thermolysis
@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.Baptize
@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.Vitriol
@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.Baptize
@AlexeyFrunze But we don't have *(&E), we have *(&E + 1).Thermolysis
@DanielFischer Is there a difference between &arr + 1 and &imaginary_array_of_100_ints_following_arr?Vitriol
Would be UB to write (int *) (&a + 1) - a, losing some type generality but not dereferencing a pointer to a non-existent object?Candelaria
@Candelaria (int*) is unnecessary, you get this type anyway, no need to cast to it one more time.Vitriol
@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.Thermolysis
@DanielFischer I don't know either what to make of it.Vitriol
@AlexeyFrunze &a + 1 has type int (*)[100]; the conversion does not happen implicitly, the code doesn't even compile without it.Candelaria
@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.Vitriol
@DanielFischer It seems you are right in telling that (&arr)[1] invokes UBHoy
N
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

Nidifugous answered 9/2, 2015 at 19:4 Comment(1)
"(&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].Hoy
M
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;

}
Mezoff answered 11/3, 2015 at 11:2 Comment(0)
H
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;

Helsa answered 29/6, 2018 at 19:52 Comment(1)
OP asked to explain how his example is working. You gave another example. That's not an answer to OP's question.Fidellas
C
-2
int main ()
{
  int arr[100];
  printf ("%d\n", ((char*)(&arr+1) - (char*)(&arr))/((char*) (arr+1) -(char*) (arr)));
  return 0;
}
Connelly answered 4/10, 2015 at 8:17 Comment(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.Funk
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.Hoy

© 2022 - 2024 — McMap. All rights reserved.