CArrays & Functions

Arrays & Functions

Passing an array to a function in C behaves very differently from passing an ordinary scalar variable like an int. This difference — known as array decay — is one of the most important, and most confusing, rules in the entire language. Misunderstanding it leads to bugs that are notoriously hard to spot, including a very common misuse of sizeof.
Arrays Decay to a Pointer
When you pass an array as a function argument, C does not copy the whole array. Instead, the array "decays" into a pointer to its first element, and it is that pointer which is actually passed to the function. The function parameter, even if it is written with array-looking syntax like int arr[], is really just a pointer, int *arr, in disguise.

C
void printSize(int arr[]) {
    // arr is really an int*, not an array, inside this function
}
sizeof Inside the Function Lies to You
sizeof(arr) gives the pointer size, not the array size
Because the parameter is actually a pointer, calling sizeof(arr) inside the function returns the size of a pointer (commonly 8 bytes on 64-bit systems) — not the size of the original array the caller passed in. This is one of the most common traps for beginners, since sizeof works perfectly on the array back in the caller's own scope.

C
#include <stdio.h>

void showSize(int arr[]) {
    printf("Inside function: %zu\n", sizeof(arr)); // size of a pointer, e.g. 8
}

int main(void) {
    int numbers[10];
    printf("Inside main: %zu\n", sizeof(numbers)); // true array size, e.g. 40

    showSize(numbers);
    return 0;
}
The Standard Workaround: Pass the Length

Since the function has no reliable way to recover how many elements the array has, the universal C convention is to pass the length as a separate parameter alongside the array.

C
#include <stdio.h>

double average(int arr[], int length) {
    int sum = 0;
    for (int i = 0; i < length; i++) {
        sum += arr[i];
    }
    return (double) sum / length;
}

int main(void) {
    int scores[5] = {90, 85, 77, 92, 88};
    int length = sizeof(scores) / sizeof(scores[0]); // compute length BEFORE decay, in main

    printf("Average: %.2f\n", average(scores, length));
    return 0;
}
Compute the length before it decays
The trick sizeof(arr) / sizeof(arr[0]) only works while arr is still a real array in its original scope — do this computation in the caller, before the array is passed anywhere, and hand the result in as a plain integer.
Modifications Inside the Function Affect the Caller

Because the function receives a pointer to the original array's memory — not a copy — any modification made to an element inside the function is visible to the caller once the function returns. This is the opposite of how ordinary scalar arguments behave in C.

C
#include <stdio.h>

void doubleValues(int arr[], int length) {
    for (int i = 0; i < length; i++) {
        arr[i] *= 2; // modifies the CALLER's memory
    }
}

void tryToChange(int x) {
    x = 999; // only modifies the local copy -- caller is unaffected
}

int main(void) {
    int nums[3] = {1, 2, 3};
    int value = 5;

    doubleValues(nums, 3);
    tryToChange(value);

    printf("%d %d %d\n", nums[0], nums[1], nums[2]); // 2 4 6 -- array WAS changed
    printf("%d\n", value);                            // 5     -- scalar was NOT changed
    return 0;
}

Argument type

What is passed

Modifications visible to caller?

Scalar (int, double, ...)

A copy of the value

No — pass by value

Array

A pointer to the first element

Yes — the function shares the caller's memory

  • An array passed to a function decays into a pointer to its first element

  • sizeof on that parameter gives the pointer's size, never the original array's size

  • Always pass the element count as a separate parameter — this is the standard C idiom

  • Because the function gets a pointer to the same memory, changes to elements ARE seen by the caller

  • This contrasts with scalars, which are passed by value and cannot be modified by the callee