Hello! Let's dive into our fourth lesson in the "Pointers and Functions" module.
In our previous lesson, we focused on how to safely return pointers from functions, emphasizing the critical difference between heap-allocated memory (safe to return) and stack-allocated local variables (unsafe to return). We saw how returning a pointer to a local variable creates a dangling pointer, a notorious source of bugs.
Today, we'll explore the other side of this interaction: passing arrays to functions efficiently using pointer parameters. This is one of the most common and fundamental uses of pointers in C. You'll see that C's approach is designed for performance, a principle highly relevant in the resource-constrained world of embedded and automotive systems.
The Illusion of Passing an Array
When you pass an array to a function in C, it looks like you're passing the entire array. But what happens under the hood is different and far more efficient. Let's start with a classic experiment that reveals the truth.
The video 'Arrays as function arguments' by mycodeschool brilliantly exposes the core concept by starting with a seemingly simple task: summing an array's elements. Watch how the sizeof operator behaves differently in the calling function versus the called function.
Watch from the beginning to 04:43. Pay close attention to the experiment where the programmer tries to calculate the array's size inside the function. Note the different outputs for sizeof(A) in main versus in sumOfElements.
As the video demonstrates, sizeof gives the expected size of the whole array (e.g., 20 bytes for 5 integers) in main, but it returns the size of a single pointer (e.g., 4 or 8 bytes) inside the function. This discrepancy is the key to understanding how arrays are passed.
Array Name "Decay"
The behavior you just saw is due to a fundamental rule in C called array decay. When an array name is used in most expressions—including passing it to a function—it "decays" into a pointer to its first element.

The C Standard is very specific about this. For a deeper, formal explanation, the C-FAQ is an invaluable resource.
The 'Arrays and Pointers' section of the C-FAQ clarifies the distinction with precision. This reading will solidify your understanding of the 'equivalence' rule.
Please read section 2.3, titled 'So what is meant by the "equivalence of pointers and arrays" in C?'. This section defines the decay rule and explains how a[i] is interpreted as *(a+i) for both arrays and pointers.
Because of this decay, a function never receives a full copy of an array. It only receives the starting address. This has three crucial consequences:
- Efficiency: Copying a large array onto the stack would be slow and could cause a stack overflow. Passing a single pointer is extremely fast and memory-efficient. In your work with ADAS, where you might handle large data buffers from cameras or RADARs, this principle is what makes processing that data feasible without massive overhead.
- Loss of Size Information: Since the function only gets a pointer, it has no information about the original array's size. This is why the
sizeoftrick failed. You must always pass the size of the array as a separate argument. - Ability to Modify: The function now has a pointer to the original data. This means it can modify the elements of the caller's array directly. This is not pass-by-value; it's an effective simulation of pass-by-reference.

Let's see how the mycodeschool video explains this mechanism.
This next segment of the video provides a clear memory diagram and explicitly states that arrays are always passed by reference.
Continue watching from 04:43 to 11:12. Focus on the stack diagram and how the function parameter A is created as a pointer variable, not a new array. The video also reinforces that int A[] and int *A are identical in a function parameter list.
As the video and C-FAQ confirm, within the context of a function parameter list, the following declarations are 100% equivalent to the compiler:void my_func(int arr[100]);void my_func(int arr[]);void my_func(int *arr);
All three declare arr as a pointer to an integer (int*). The array-like syntax [] is just "syntactic sugar" to make the function's intent clearer to human readers.
To see the modification aspect in action, the final part of the video demonstrates a function that doubles each element in the original array.
This short concluding clip shows a practical example of modifying the caller's array.
Watch from 11:12 to the end (12:44). This demonstrates that changes made inside the doubleElements function are reflected back in main.
Test your understanding!
Consider the following code, which is supposed to clear (set to zero) an array of error flags.
#include <stdio.h>
void clear_flags(unsigned char *flags) {
// There is a bug on the next line.
size_t num_flags = sizeof(flags) / sizeof(flags[0]);
for (size_t i = 0; i < num_flags; ++i) {
flags[i] = 0;
}
}
int main() {
unsigned char error_flags[16] = {1, 1, 0, 1, /* ...and so on */};
clear_flags(error_flags);
// Check if the first flag is cleared
if (error_flags[0] == 0) {
printf("First flag cleared.\n");
} else {
printf("First flag NOT cleared. Value: %u\n", error_flags[0]);
}
return 0;
}
- What will this program most likely print on a 64-bit system, and why?
- How would you fix the
clear_flagsfunction and the call to it inmain?
Show answer
-
On a 64-bit system, a pointer (
flags) is 8 bytes.sizeof(flags[0])(anunsigned char) is 1 byte. Therefore,num_flagswill be calculated as8 / 1 = 8. The loop will only run 8 times, not 16. The program will print "First flag cleared." because it does successfully clear the first 8 elements, but it fails to clear the entire array. This is a subtle bug that might go unnoticed if you only test the first few elements. -
You must pass the size of the array as a separate argument.
Corrected Code:
#include <stdio.h> #include <stddef.h> // For size_t // Corrected function signature void clear_flags(unsigned char *flags, size_t num_flags) { for (size_t i = 0; i < num_flags; ++i) { flags[i] = 0; } } int main() { unsigned char error_flags[16] = {1, 1, 0, 1}; size_t num_elements = sizeof(error_flags) / sizeof(error_flags[0]); // Corrected function call clear_flags(error_flags, num_elements); // Let's check the last flag to be sure if (error_flags[15] == 0) { printf("Last flag cleared successfully.\n"); } else { printf("Last flag NOT cleared.\n"); } return 0; }
Passing Multi-Dimensional Arrays
This is where many developers get tripped up. Given that a 1D array decays to a pointer, it's a common but incorrect assumption that a 2D array int arr[3][4] decays to a pointer-to-a-pointer (int**).
A 2D array is an "array of arrays". So, an array of [array of 4 ints] decays to a pointer to an [array of 4 ints].
The syntax for this is int (*p)[4]. This declares p as a pointer to an array of 4 integers.
This distinction is vital because the compiler needs to know the size of all dimensions except the first to calculate memory offsets. To access arr[i][j], the compiler computes the address: base_address + (i * NUM_COLUMNS + j) * sizeof(element). Without NUM_COLUMNS, this calculation is impossible.
Let's see this explained visually.
Pointers and multidimensional arrays
The video 'Pointers and multidimensional arrays' from mycodeschool directly addresses this topic. We'll skip to the relevant part about function passing.
Watch from 13:24 to 16:43. This section explains the correct syntax for passing 1D, 2D, and 3D arrays to functions, emphasizing why you must specify the inner dimensions.
This leads to the correct function signatures for a 2D array int arr[ROWS][COLS]:void my_func(int arr[ROWS][COLS]);void my_func(int arr[][COLS]);void my_func(int (*arr)[COLS]);
Again, all three are equivalent. Notice that the number of columns (COLS) must be specified. Only the first dimension (ROWS) can be omitted.
The C-FAQ provides the definitive textual explanation for this rule.
Let's consult the C-FAQ again for a precise rule on this matter.
Read section 2.10, 'My compiler complained when I passed a two-dimensional array to a routine expecting a pointer to a pointer.' This section gives the correct function declarations and explains why they are necessary.
In your work with AUTOSAR, if you were to define a software component that processes a signal group represented as a 2D array in an ARXML file, the generated RTE (Runtime Environment) function stub would follow these rules precisely, creating a function signature with a pointer to an array of a fixed size.
Conclusion
You've now seen how C handles passing arrays to functions—a design choice that prioritizes performance and memory efficiency by passing pointers instead of making copies.
Key Takeaways:
- Array Decay: When passed to a function, an array name "decays" into a pointer to its first element.
- Efficiency: This mechanism avoids copying large amounts of data, which is critical for performance-sensitive applications like those in automotive systems.
- Pass the Size: Because the function only receives a pointer, it loses size information. You must always pass the array's size as a separate parameter.
- Modification: The function receives a pointer to the original data and can therefore modify the caller's array.
- Multi-Dimensional Arrays: A 2D array
T arr[R][C]decays to a pointer to an array,T (*p)[C]. All dimensions except the first must be specified in the function parameter so the compiler can perform address arithmetic.
In our next lesson, we will leverage the modification capability we explored today to solve a common C problem: how to use pointer parameters to return multiple values from a function.