Dan
Dan

Reputation: 627

What is the point of specifying the size of an array in a parameter declaration?

#include <stdio.h>

int a[] = {1,2};

void test(int in[3]){
  //
}
 
int main() {
 test(a); 
 return 0;
}

In the above code int in[3] is the same as int *in. The number 3 doesn't really do anything and it's not even the correct size, but even so the compiler doesn't complain. So is there a reason this syntax is accepted in C or I'm missing a functionality?

Upvotes: 7

Views: 1196

Answers (4)

Eric Postpischil
Eric Postpischil

Reputation: 222447

When an array parameter declaration contains a constant size, the only purpose it can serve is as documentation for readers, by indicating to them what size array the function expects. For a constant expression n, the compiler converts an array declaration such as int in[n] to int *in, after which there is no difference to the compiler and so nothing is affected by the value of n.

Originally in C, function parameters were specified by a list of declarations after the initial function declaration, such as:

int f(a, b, c)
int a;
float b;
int c[3];
{
    … function body
}

I conjecture that array sizes were permitted in these declarations simply because they used the same grammar as other declarations. It would have been harder to write compiler code and documentation that excluded the sizes than to simply allow them to occur but to ignore them. When declaring paramter types inside function prototypes (int f(int a, float b, int c[3])) was introduced, I conjecture the same reasoning applied.

However:

  • If the declaration contains static, as in int in[static n], then, when the function is called, the corresponding argument must point to at least n elements, per C 2018 6.7.6.3 7. Compilers may use this for optimization.
  • If the array size is not a constant, it may be evaluated by the compiler when the function is called. For example, if the function declaration is void test(int in[printf("Hi")]), then both GCC 10.2 and Apple Clang 11.0 print “Hi” when the function is called. (However, it is not clear to me the C standard requires this evaluation.)
  • This adjustment occurs only for the actual array parameter, not arrays within it. For example, in the parameter declaration int x[3][4], the type of x is adjusted to be int (*)[4]. The 4 remains a part of the size and has effects on pointer arithmetic with x.
  • When a parameter is declared as an array, the element type must be complete. In contrast, a parameter declared as a pointer need not point to a complete type. For example, struct foo x[3] yields a diagnostic message if struct foo has not been fully defined, but struct foo *x does not.

Upvotes: 5

Krishna Kanth Yenumula
Krishna Kanth Yenumula

Reputation: 2567

If we specify the size of the array in the function definition, it can be used for checking errors using static analysis tool. I used cppcheck tool for the following code.

#include <stdio.h>
void test(int in[3])
{
  in[3] = 4;
}

The output is:

Cppcheck 2.2
[test.cpp:4]: (error) Array 'in[3]' accessed at index 3, which is out of bounds.
Done!

But, if you donot give any size, you will not get any error from cppcheck .

#include <stdio.h>
void test(int in[])
{
   in[3] = 4;
}

The output is :

Cppcheck 2.2
Done!

But , in general, there is no need to specify the size of the array, in function definition. We cannot find the size of array inside another function, using sizeof operator, because only value of the pointer is copied. Hence, input of sizeof operator will be of type int* and not of type int[] (inside the function test()). So, the value of the size of array does not effect the code. See the code below :

#include <stdio.h>

int a[] = {1, 2, 3, 4, 5, 6, 7, 8};

void test(int in[8]) // Same as void test(int *arr)
{
    unsigned int n = sizeof(in) / sizeof(in[0]); // sizeof(int*)/sizeof(int)
    printf("Array size inside test() is %d\n", n);
}

int main()
{
    unsigned int n = sizeof(a) / sizeof(a[0]);  //sizeof(int[])/sizeof(int)
    printf("Array size inside main() is %d\n", n);
    test(a);
    return 0;
}

The output is:

Array size inside main() is 8
Array size inside test() is 2

So, we need to pass the size of an array with an another variable.

Upvotes: 2

Lundin
Lundin

Reputation: 213689

As far as the C language and compiler cares, it doesn't matter if you specify the size since the array gets adjusted to a pointer to the first element anyway.

However, stating the size can improve the ability for static analysis by external tools other than the compiler. For example, a static analyser can easily tell that this is an array out-of-bounds bug:

void test(int in[3]){
  in[3] = 0;
}

But it has no idea if this is a bug:

void test(int* in){
  in[3] = 0;
}

Related to this, the non-existent type safety between different array sizes can actually be solved by using a trick - pass arrays by pointer instead. Because a pointer to an array does not decay and is picky about getting handed the right size. Example:

void test(int (*in)[3]){
  int* ptr = *in;
  ptr[3] = 0;
}

int foo[10];
test(&foo);  // compiler error

int bar[3];
test(&bar);  // ok

However, this trick makes the code a bit harder to read and understand.

Upvotes: 1

Simson
Simson

Reputation: 3559

In C, there is no difference between a pointer to one structure and a pointer to an array of the same data structure. To get the start address of the next you simply increment the pointer with the size of the data and since it is impossible to determine the size from only the pointer itself you must supply this as a programmer.

Lets try to modify the program

#include <stdio.h>

void test(int in[3]){
  printf("%d %d,%d,%d\n",in[0],in[1],in[2],in[3]); // !Sic bug intentional 
}
int main() {
 int a[] = {1,2};
 int b[] = {3,4};
 test(a); 
 test(b); 
 return 0;
}

And run it:

$ gcc pointer_size.c  -o a.out  && ./a.out 

1 2,3,4
3 4,-1420617472,-1719256057

In this case the arrays are placed back to back to each other, so reading at index 2 and 3 from a will yield the data from b and when we read too much from b whatever present on those addresses will be read.

This is a very common source for security vulnerabilities even to this date.

Upvotes: 1

Related Questions