This article is for C programming newbies and enthusiasts. It deals with how arrays are passed as parameters to a function decays into pointers.
Many newbies to the C world are often confused about passing an array as the parameter to a function. They find it difficult to decide whether it is done using the ‘pass by value’ or ‘pass by reference’ parameter passing mechanism. This article explains how the arrays are passed to the function and why this is so.
Example 1: ‘An array name as a function parameter is a pointer’
In this example, consider 1-D array to explain the meaning of the above statement with the help of the demo codes. Consider Code 1, which contains a function called display() to display the elements of the array.
1 #include <stdio.h> 2 3 void display(int numbers[], int size); 4 5 int main() 6 { 7 //Defination of an array 8 int numbers[] = {10, 20, 30, 40, 50}; 9 10 //Calling display function, to print the elements 11 display(numbers, sizeof(numbers) / sizeof(numbers[0])); 12 13 return 0; 14 } 15 16 //Function definition 17 void display(int numbers[], int size) 18 { 19 int i; 20 for (i = 0; i < size; i++) 21 { 22 printf(“The value @ address: %p: %d\n”, &numbers[i], numbers[i]); 23 } 24 }
Code 1: This code is to demonstrate how arrays are passed to functions
From Code 1, whenever arrays are passed as the arguments to functions, they are always passed by using the ‘Pass by reference’ mechanism. Because of this, they will decay into pointers in the function parameters. In Line 17 of Code 1, In main function, the name of array (numbers) which contains the base address of the array (numbers) is passed to the function called the display, and collected using the parameter called numbers, which is nothing but the pointer to the base address of the array (numbers) defined in the main function. The function display() is equivalent to the code shown below.
void display(int *numbers, int size);
The definition of the display is also given in Code 2.
1 //Function definition 2 void display(int *numbers, int size) 3 { 4 int i; 5 for (i = 0; i < size; i++) 6 { 7 printf("The value @ address: %p: %d\n", &numbers[i], numbers[i]); 8 } 9 }
Code 2
The two declarations of the function display shown in Code 1 and Code 2 look different from the programmer’s perspective, but from the compiler’s viewpoint, both are one and the same. The standard specifies that a parameter in the function declaration, which is of ‘array of type(T)’ shall be decayed to ‘pointer to type(T)’.
Note 1: An array name in the declaration of a function parameter is treated by the compiler as a pointer to the first element of the array.
void display(int numbers[], int size); void display(int numbers[5], int size);
In the code given above, all the function declarations are equivalent. One can call the function display with an array, with a pointer or with its actual argument. In all the three prototypes, the function parameter (numbers) will decay into the ‘Pointer to the first element of an array’.
Example 2
Consider the example of a 2D array, to explain how it will decay into the ‘Pointer to an array’, when passed to the function. Let us start with a demo code as shown in Code 3:
1 #include <stdio.h> 2 #define ROW 3 3 #define COL 2 4 5 int main() 6 { 7 int array[ROW][COL]; 8 populate(array); 9 10 //Some code here 11 } 12 13 void populate(int array[ROW][COL]) 14 { 15 //Some code here 16 }
Code 3: Code to demonstrate how 2D arrays are passed to functions
From the example shown in Code 3, when the 2D array of type(T) is passed as a parameter to the function, it will decay into the ‘Pointer to an array’, as shown in Line 13 of Code 3. Code 3 shows a straightforward way of passing the 2D arrays to the function, which means declaring them exactly the same way as declared in the calling function.
1 #include <stdio.h> 2 #define ROW 3 3 #define COL 2 4 5 int main() 6 { 7 int array[ROW][COL]; 8 populate(array); 9 10 //Some code here 11 } 12 13 void populate(int (*array)[COL]) 14 { 15 //Some code here 16 }
Code 4: Code to demonstrate how 2D arrays are passed to functions
As far as we are concerned, with the equivalence between pointers and arrays, when arrays are passed as an argument to a function, what really gets passed is a pointer to the array’s first element. When we declare a function that accepts an array as a parameter, the compiler simply compiles the function as if that parameter were a pointer, since a pointer is what it will actually receive.
In general, when multi-dimensional arrays are passed as a parameter to the function, what actually is passed is a pointer to the array’s first element. Since the first element of a multi-dimensional array is another array, what gets passed to the function is a ‘pointer to an array’.
We cannot receive a 2D array as shown in the demo Code 5, since compilers will generate code in such a way that functions will receive the 2D array as ‘Pointer to an array’ and not ‘Pointer to Pointer’.
1 #include <stdio.h> 2 #define ROW 3 3 #define COL 2 4 5 int main() 6 { 7 int array[ROW][COL]; 8 populate(array); 9 10 //Some code here 11 } 12 13 void populate(int **array) //Error 14 { 15 //Some code here 16 }
Code 5: Code to demonstrate that 2D arrays cannot be collected as pointer to pointer
Now, consider Code 6 given below:
1 #include <stdio.h> 2 3 int main() 4 { 5 //Define the array 6 int a[5] = {10, 20, 30, 40, 50}; 7 8 int x = *a; 9 10 ... 11 12 return 0; 13 }
Code 6
From Code 6, in the expression shown in Line 8, the name of the array ‘a’ is decayed into the pointer to the first element of the array by the compiler. As a result, dereferencing the pointer yields the value stored at the first location of an array, which is nothing but 10 in the example shown in Code 6.
Let us consider one more example to show how the ‘subscript in the array is always the offset from the pointer’.
1 #include <stdio.h> 2 3 int main() 4 { 5 //Define the array 6 int a[5] = {10, 20, 30, 40, 50}; 7 8 int x = a[2]; 9 10 // a[2] <=> *(a + 2) 11 12 return 0; 13 }
Code 7
From Code 7, Line 8, the compiler internally treats the name of the array ‘a’ as the pointer to the first element of the array; the subscript value 2 is added to the pointer as a offset to get the new address. The equivalent of a[2] is nothing but *(a + 2). The pictorial representation of Code 7 is shown in Table 1. So, here, once again, the name of the array decays into the pointer to the first element of the array and then the subscript will be added.
The compiler automatically scales a subscript to the size of the object pointed at. For instance, if sizeof(int) is 4 bytes long, then the subscript 2 in a[2] is actually 2 * 4 bytes long from the base address of the array. The compiler takes care of scaling, before adding the subscript to the base address of the array. The detailed explanation is given in Table 1.
An array name when passed as an argument to the function, it decays into a pointer to the first element of the arry by the compiler.
An array name in an expression (apart from the place of declaration) is also decayed by the compiler to a pointer to the first element of the array. A subscript is always equivalent to an offset from a pointer. 2D arrays cannot be collected in pointer-to-pointer when passed as an argument to the functions.