When Do We Need to Pass the Size of Array as a Parameter

when do we need to pass the size of array as a parameter

First, an array passed to a function actually passes a pointer to the first element of the array, e.g., if you have

int a[] = { 1, 2, 3 };
f(a);

Then, f() gets &a[0] passed to it. So, when writing your function prototypes, the following are equivalent:

void f(int arr[]);
void f(int *arr);

This means that the size of the array is lost, and f(), in general, can't determine the size. (This is the reason I prefer void f(int *arr) form over void f(int arr[]).)

There are two cases where f() doesn't need the information, and in those two cases, it is OK to not have an extra parameter to it.

First, there is some special, agreed value in arr that both the caller and f() take to mean "the end". For example, one can agree that a value 0 means "Done".

Then one could write:

int a[] = { 1, 2, 3, 0 }; /* make sure there is a 0 at the end */
int result = f(a);

and define f() something like:

int f(int *a)
{
size_t i;
int result = 0;
for (i=0; a[i]; ++i) /* loop until we see a 0 */
result += a[i];
return result;
}

Obviously, the above scheme works only if both the caller and the callee agree to a convention, and follow it. An example is strlen() function in the C library. It calculates the length of a string by finding a 0. If you pass it something that doesn't have a 0 at the end, all bets are off, and you are in the undefined behavior territory.

The second case is when you don't really have an array. In this case, f() takes a pointer to an object (int in your example). So:

int change_me = 10;
f(&change_me);
printf("%d\n", change_me);

with

void f(int *a)
{
*a = 42;
}

is fine: f() is not operating on an array anyway.

Why should I declare a C array parameter's size in a function header?

Can anyone enlighten me as to why I should bother to specify the size of a C array argument in a function header? For example:

void foo (const char sz[6]) { sz[42] = 43; }

IMO, you shouldn't. When you try to pass an array to a function, what's really passed is a pointer to the beginning of the array. Since what the function receives will be a pointer, it's better to write it to make that explicit:

void foo(char const *sz)

Then, since it's now clear that the function has been given no clue of the size, add that as a separate parameter:

void foo(char const *sz, size_t size)

Forcing an array size in a function parameter in C when passing an array

If you pass a pointer to the array instead of a pointer to its first element, you will get an incompatible pointer warning:

void foo(int (*bar)[42])
{}

int main(void)
{
int a[40];
foo(&a); // warning: passing argument 1 of 'foo' from incompatible pointer type [-Werror=incompatible-pointer-types]
// note: expected 'int (*)[42]' but argument is of type 'int (*)[40]'

int b[45];
foo(&b); // warning: passing argument 1 of 'foo' from incompatible pointer type [-Werror=incompatible-pointer-types]
// note: expected 'int (*)[42]' but argument is of type 'int (*)[45]'
}

Compile with -Werror to make it an error.

godbolt

Why do we need to specify the column size when passing a 2D array as a parameter?

When it comes to describing parameters, arrays always decay into pointers to their first element.

When you pass an array declared as int Array[3] to the function void foo(int array[]), it decays into a pointer to the beginning of the array i.e. int *Array;. Btw, you can describe a parameter as int array[3] or int array[6] or even int *array - all these will be equivalent and you can pass any integer array without problems.

In case of arrays of arrays (2D arrays), it decays to a pointer to its first element as well, which happens to be a single dimensional array i.e. we get int (*Array)[3].

Specifying the size here is important. If it were not mandatory, there won't be any way for compiler to know how to deal with expression Array[2][1], for example.

To dereference that a compiler needs to compute the offset of the item we need in a contiguous block of memory (int Array[2][3] is a contiguous block of integers), which should be easy for pointers. If a is a pointer, then a[N] is expanded as start_address_in_a + N * size_of_item_being_pointed_by_a. In case of expression Array[2][1] inside a function (we want to access this element) the Array is a pointer to a single dimensional array and the same formula applies. The number of bytes in the last square bracket is required to find size_of_item_being_pointed_by_a. If we had just Array[][] it would be impossible to find it out and hence impossible to dereference an array element we need.

Without the size, pointers arithmetics wouldn't work for arrays of arrays. What address would Array + 2 produce: advance the address in Array 2 bytes ahead (wrong) or advance the pointer 3* sizeof(int) * 2 bytes ahead?

C sizeof a passed array

There is no magic solution. C is not a reflective language. Objects don't automatically know what they are.

But you have many choices:

  1. Obviously, add a parameter
  2. Wrap the call in a macro and automatically add a parameter
  3. Use a more complex object. Define a structure which contains the dynamic array and also the size of the array. Then, pass the address of the structure.

Why can one specify the size of an array in a function parameter?

In C++ (as well as in C), parameters declared with array type always immediately decay to pointer type. The following three declarations are equivalent

void printValues(int nums[3], int length);
void printValues(int nums[], int length);
void printValues(int *nums, int length);

I.e. the size does not matter. Yet, it still does not mean that you can use an invalid array declaration there, i.e. it is illegal to specify a negative or zero size, for example.

(BTW, the same applies to parameters of function type - it immediately decays to pointer-to-function type.)

If you want to enforce array size matching between arguments and parameters, use pointer- or reference-to-array types in parameter declarations

void printValues(int (&nums)[3]);
void printValues(int (*nums)[3]);

Of course, in this case the size will become a compile-time constant and there's no point of passing length anymore.

Why isn't the size of an array parameter the same as within main?

An array-type is implicitly converted into pointer type when you pass it in to a function.

So,

void PrintSize(int p_someArray[10]) {
printf("%zu\n", sizeof(p_someArray));
}

and

void PrintSize(int *p_someArray) {
printf("%zu\n", sizeof(p_someArray));
}

are equivalent. So what you get is the value of sizeof(int*)



Related Topics



Leave a reply



Submit