Length of array in function argument

Solution 1:

sizeof only works to find the length of the array if you apply it to the original array.

int a[5]; //real array. NOT a pointer
sizeof(a); // :)

However, by the time the array decays into a pointer, sizeof will give the size of the pointer and not of the array.

int a[5];
int * p = a;
sizeof(p); // :(

As you have already smartly pointed out main receives the length of the array as an argument (argc). Yes, this is out of necessity and is not redundant. (Well, it is kind of reduntant since argv is conveniently terminated by a null pointer but I digress)

There is some reasoning as to why this would take place. How could we make things so that a C array also knows its length?

A first idea would be not having arrays decaying into pointers when they are passed to a function and continuing to keep the array length in the type system. The bad thing about this is that you would need to have a separate function for every possible array length and doing so is not a good idea. (Pascal did this and some people think this is one of the reasons it "lost" to C)

A second idea is storing the array length next to the array, just like any modern programming language does:

a -> [5];[0,0,0,0,0]

But then you are just creating an invisible struct behind the scenes and the C philosophy does not approve of this kind of overhead. That said, creating such a struct yourself is often a good idea for some sorts of problems:

struct {
    size_t length;
    int * elements;
}

Another thing you can think about is how strings in C are null terminated instead of storing a length (as in Pascal). To store a length without worrying about limits need a whopping four bytes, an unimaginably expensive amount (at least back then). One could wonder if arrays could be also null terminated like that but then how would you allow the array to store a null?

Solution 2:

The array decays to a pointer when passed.

Section 6.4 of the C FAQ covers this very well and provides the K&R references etc.


That aside, imagine it were possible for the function to know the size of the memory allocated in a pointer. You could call the function two or more times, each time with different input arrays that were potentially different lengths; the length would therefore have to be passed in as a secret hidden variable somehow. And then consider if you passed in an offset into another array, or an array allocated on the heap (malloc and all being library functions - something the compiler links to, rather than sees and reasons about the body of).

Its getting difficult to imagine how this might work without some behind-the-scenes slice objects and such right?


Symbian did have a AllocSize() function that returned the size of an allocation with malloc(); this only worked for the literal pointer returned by the malloc, and you'd get gobbledygook or a crash if you asked it to know the size of an invalid pointer or a pointer offset from one.

You don't want to believe its not possible, but it genuinely isn't. The only way to know the length of something passed into a function is to track the length yourself and pass it in yourself as a separate explicit parameter.

Solution 3:

As stated by @Will, the decay happens during the parameter passing. One way to get around it is to pass the number of elements. To add onto this, you may find the _countof() macro useful - it does the equivalent of what you've done ;)

Solution 4:

First, a better usage to compute number of elements when the actual array declaration is in scope is:

sizeof array / sizeof array[0]

This way you don't repeat the type name, which of course could change in the declaration and make you end up with an incorrect length computation. This is a typical case of don't repeat yourself.

Second, as a minor point, please note that sizeof is not a function, so the expression above doesn't need any parenthesis around the argument to sizeof.

Third, C doesn't have references so your usage of & in a declaration won't work.

I agree that the proper C solution is to pass the length (using the size_t type) as a separate argument, and use sizeof at the place the call is being made if the argument is a "real" array.

Note that often you work with memory returned by e.g. malloc(), and in those cases you never have a "true" array to compute the size off of, so designing the function to use an element count is more flexible.

Solution 5:

Regarding int main():

According to the Standard, argv points to a NULL-terminated array (of pointers to null-terminated strings). (5.1.2.2.1:1).

That is, argv = (char **){ argv[0], ..., argv[argc - 1], 0 };.

Hence, size calculation is performed by a function which is a trivial modification of strlen().

argc is only there to make argv length calculation O(1).

The count-until-NULL method will NOT work for generic array input. You will need to manually specify size as a second argument.