20

I want to get the length of an array, say int array[] = {1, 2, 3, 4}. I used sizeof to do that.

int length(int array[])
{
     return sizeof(array) / sizeof(int);
}

int main()
{
    int array[] = {1, 2, 3, 4};
    printf("%d\n", length(array)); // print 1
    printf("%d\n", sizeof(array) / sizeof(int)); // print 4
}

So, why the sizeof(array) in function length returns the pointer size of array? But in function main, it works.

And, how should I modify the length function to get an array's length?

xiaowl
  • 5,177
  • 3
  • 27
  • 28
  • 1
    Yes, I know this is a C++ question, but it contains very useful information concerning C as well: http://stackoverflow.com/questions/4810664/how-do-i-use-arrays-in-c – chris Jul 23 '12 at 23:47

8 Answers8

26

A special C rule says that for function parameters, array types are adjusted to pointer types. That means:

int length(int array[]);

is equivalent to

int length(int *array);

So when you compute the sizeof the array you are actually computing the size of the pointer.

(C99, 6.7.5.3p7) "A declaration of a parameter as "array of type" shall be adjusted to "qualified pointer to type", where the type qualifiers (if any) are those specified within the [ and ] of the array type derivation."

ouah
  • 142,963
  • 15
  • 272
  • 331
  • 7
    ...and the implication of this is that you cannot rewrite `length()` to work as you want - if a function needs to know the size of an array, it needs to be passed to that function as a separate parameter. – caf Jul 24 '12 at 00:16
  • @caf I agree with you. For additional information, it makes sense to do it this way, especially when on an ARM processor with r0, r1, r2, r3 32-bit registers. Therefore a pointer can be passed in r0 rather than having to use stack. –  Oct 17 '16 at 23:29
6

The array decays into a pointer when you pass it into a function.

tskuzzy
  • 35,812
  • 14
  • 73
  • 140
6

As the other answerers have noted, a function declared to take an array is compiled the same as if it took a pointer.

The most common way I've seen to do your length method is with a #define, but if you play tricks with templates, you can make your function work.

template <size_t _Size> inline int length(int(& array)[_Size])
{
    //return sizeof(array) / sizeof(int);
    return _Size;
};

int array[] = {1, 2, 3, 4};
printf("%d\n", length(array)); // print 4

With your array of length 4, this gets compiled as a method that takes an array of length 4, and returns a static 4. This also has the advantage that if you try to pass something that isn't an array, the compiler will give an error, whereas the #define method doesn't.

int* foo = array;
printf("%d\n", length(foo)); 
    // error C2784: 'int length(int (&)[_Size])' : could not deduce template
    //     argument for 'int (&)[_Size]' from 'int *'
    // test.cpp(56) : see declaration of 'length'
David Yaw
  • 27,383
  • 4
  • 60
  • 93
3

In the main program, the compiler knows the length of the array. In the subroutine, it doesn't. In particular, in a subroutine, a parameter of type int[] is identical to a parameter of type int *. In the main program, on the other hand, array has type int[4]. (You can test this by trying to assign another value to array in side main. The compiler will complain.)

Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
2

C does not store runtime metadata about the size of an array with the variable. You will have to store that yourself somewhere.

A variable of type int[] in the context of your method length is just a pointer to a block of memory. That's why the size is the same as the size of any other pointer on your system.

When sizeof has access to the array at compile-time (as in your main function) it will instead return the number of bytes occupied by the array.

Eric J.
  • 147,927
  • 63
  • 340
  • 553
  • 1
    "A variable of type int[] is just a pointer to a block of memory" - that's incorrect in general case. A variable of type `int[]` *is* a block of memory. It is not a pointer. The only contexts where `int[]` stands for a pointer is function parameter declaration and it is a *special case*. – AnT stands with Russia Jul 23 '12 at 23:54
  • Clarified that it is a pointer *in the context of the method length()*. Thanks for pointing that out. – Eric J. Jul 24 '12 at 00:50
2

If you want length to be aware of the true size of the passed in array, that information has to be passed in, as everyone says. But it is possible for it to receive a reference to the array with the information also built-in:

int length(int n, int (* array)[n])
{
     return sizeof(*array) / sizeof(int);
}

int main()
{
    int array[] = {1, 2, 3, 4};
    printf("%d\n", length(4, &array)); // print 1
    printf("%d\n", sizeof(array) / sizeof(int)); // print 4
}

I don't know what good that actually does you, though, since the size is already provided in the first place. But you see that the sizeof operator works as you wanted it to in this case.

jxh
  • 69,070
  • 8
  • 110
  • 193
0

Because the variable array inside length is not the same as the one outside. You have to keep the sizeof(array) / sizeof(int) in the main function.

AlexDev
  • 4,049
  • 31
  • 36
0

To modify your code so that length can get the array's length, turn it into a macro:

#define length(x) (sizeof(x)/sizeof(*x))

For your code sample, this will behave as you expect.

Note that this will only work on statically-allocated arrays, and only when called using the original array (it won't work when using a pointer to the array). This is because when you write sizeof(x) where x is a pointer to an array, the compiler interprets it literally. It doesn't have any way of mapping x back to the array that it points to before evaluating the sizeof.

bta
  • 43,959
  • 6
  • 69
  • 99