GuideC Programming

Passing Arrays To Functions

Arrays / Passing Arrays To Functions

Concept Lesson
Intermediate
4 min

Learning Objective

Understand Passing Arrays To Functions well enough to explain it, recognize it in C Programming, and apply it in a small task.

Why It Matters

This concept is part of the foundation that later lessons and projects assume you already understand.

FunctionsTable Of ContentsHow Arrays Are PassedKey Concept: Arrays Decay To PointersImplications
Private notes
0/8000

Notes stay private to your browser until account sync is configured.

Guide
3 min read18 headings

Passing Arrays to Functions in C

Table of Contents

  1. Introduction
  2. How Arrays are Passed
  3. Passing 1D Arrays
  4. Passing 2D Arrays
  5. Passing Arrays by Reference
  6. Array Size in Functions
  7. Returning Arrays from Functions
  8. Common Patterns
  9. Best Practices
  10. Summary

Introduction

Passing arrays to functions is essential for writing modular and reusable code. Understanding how C handles array parameters is crucial because arrays behave differently than regular variables when passed to functions.


How Arrays are Passed

Key Concept: Arrays Decay to Pointers

When you pass an array to a function, C does NOT copy the entire array. Instead, it passes the address of the first element (the array "decays" to a pointer).

int arr[5] = {1, 2, 3, 4, 5};
func(arr);

What happens:
ā”Œā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”
│ 1 │ 2 │ 3 │ 4 │ 5 │  ← Original array in memory
ā””ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”˜
  ↑
  │
  └── Address passed to function (pointer to first element)

Implications

AspectRegular VariablesArrays
Passed byValue (copy)Reference (address)
Changes in functionDon't affect originalAffect original
Memory overheadCopy of dataOnly address (8 bytes)
Size info preservedYesNo (decays to pointer)

Passing 1D Arrays

Method 1: Array Notation

void printArray(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

int main(void) {
    int numbers[5] = {10, 20, 30, 40, 50};
    printArray(numbers, 5);
    return 0;
}

Method 2: Pointer Notation

void printArray(int *arr, int size) {
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);  // or *(arr + i)
    }
    printf("\n");
}

Method 3: Sized Array (Size Ignored)

void printArray(int arr[100], int size) {
    // The [100] is ignored by compiler!
    // It's treated as int *arr
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
}

All Three Are Equivalent

void func(int arr[]);    // Array notation
void func(int *arr);     // Pointer notation
void func(int arr[100]); // Sized (size ignored)

// All three declare the same function!

Passing 2D Arrays

Important Rule

For 2D arrays, all dimensions except the first must be specified.

int matrix[3][4];

To pass: func(matrix, 3);

Function must know column count to calculate:
Address of [i][j] = Base + (i Ɨ COLS + j) Ɨ sizeof(int)
                          ↑
                    Must know COLS!

Method 1: Full Dimensions

void printMatrix(int arr[3][4]) {
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 4; j++) {
            printf("%d ", arr[i][j]);
        }
        printf("\n");
    }
}

Method 2: Omit First Dimension

void printMatrix(int arr[][4], int rows) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < 4; j++) {
            printf("%d ", arr[i][j]);
        }
        printf("\n");
    }
}

Method 3: Pointer to Array

void printMatrix(int (*arr)[4], int rows) {
    // arr is pointer to array of 4 ints
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < 4; j++) {
            printf("%d ", arr[i][j]);
        }
        printf("\n");
    }
}

Method 4: Variable Length Arrays (C99+)

void printMatrix(int rows, int cols, int arr[rows][cols]) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            printf("%d ", arr[i][j]);
        }
        printf("\n");
    }
}

// Usage
int matrix[3][4] = {...};
printMatrix(3, 4, matrix);

Visual Comparison

Declaration              Meaning
───────────────────────────────────────────────────
int arr[]               Pointer to int
int arr[10]             Pointer to int (10 ignored)
int arr[][4]            Pointer to array of 4 ints
int arr[3][4]           Pointer to array of 4 ints
int (*arr)[4]           Pointer to array of 4 ints

Passing Arrays by Reference

Since arrays decay to pointers, modifications in the function affect the original array.

Modifying Array Elements

void doubleElements(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        arr[i] *= 2;  // Modifies original!
    }
}

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

    printf("Before: ");
    for (int i = 0; i < 5; i++) printf("%d ", numbers[i]);

    doubleElements(numbers, 5);

    printf("\nAfter:  ");
    for (int i = 0; i < 5; i++) printf("%d ", numbers[i]);
    // Output: 2 4 6 8 10

    return 0;
}

Preventing Modification with const

void printArray(const int arr[], int size) {
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
        // arr[i] = 10;  // ERROR! Cannot modify const
    }
}

Why Use const?

1. Documents intent - "this function only reads"
2. Prevents accidental modification
3. Allows compiler optimizations
4. Can pass const arrays to the function

Array Size in Functions

The Problem

When an array decays to a pointer, size information is lost.

void func(int arr[]) {
    // sizeof(arr) gives pointer size (8 bytes), NOT array size!
    int size = sizeof(arr);  // WRONG! This is pointer size
}

The Solution: Pass Size Separately

void processArray(int arr[], int size) {
    // Use the size parameter
    for (int i = 0; i < size; i++) {
        // process arr[i]
    }
}

int main(void) {
    int data[100];
    int size = sizeof(data) / sizeof(data[0]);  // Calculate here
    processArray(data, size);
    return 0;
}

Common Pattern: Size Calculation

#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))

int main(void) {
    int numbers[] = {1, 2, 3, 4, 5};
    processArray(numbers, ARRAY_SIZE(numbers));
    return 0;
}

Returning Arrays from Functions

You Cannot Directly Return Arrays

// WRONG! Cannot return array
int[] getArray(void) {
    int arr[5] = {1, 2, 3, 4, 5};
    return arr;  // Error!
}

Method 1: Modify Passed Array

void fillArray(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        arr[i] = i + 1;
    }
}

int main(void) {
    int result[5];
    fillArray(result, 5);  // result is filled
    return 0;
}
int* getArray(void) {
    static int arr[5] = {1, 2, 3, 4, 5};
    return arr;  // OK because static persists
}
// Warning: Same array returned every call!

Method 3: Dynamic Allocation (Advanced)

int* createArray(int size) {
    int *arr = malloc(size * sizeof(int));
    for (int i = 0; i < size; i++) {
        arr[i] = i + 1;
    }
    return arr;
}

int main(void) {
    int *arr = createArray(5);
    // Use arr...
    free(arr);  // Don't forget!
    return 0;
}

Method 4: Return Through Struct

#define MAX_SIZE 100

typedef struct {
    int data[MAX_SIZE];
    int size;
} IntArray;

IntArray createArray(int n) {
    IntArray result = {.size = n};
    for (int i = 0; i < n; i++) {
        result.data[i] = i + 1;
    }
    return result;
}

Common Patterns

Pattern 1: Search Function

int findElement(const int arr[], int size, int target) {
    for (int i = 0; i < size; i++) {
        if (arr[i] == target) {
            return i;  // Found at index i
        }
    }
    return -1;  // Not found
}

Pattern 2: Aggregation Functions

int sum(const int arr[], int size) {
    int total = 0;
    for (int i = 0; i < size; i++) {
        total += arr[i];
    }
    return total;
}

double average(const int arr[], int size) {
    if (size == 0) return 0.0;
    return (double)sum(arr, size) / size;
}

int max(const int arr[], int size) {
    int maxVal = arr[0];
    for (int i = 1; i < size; i++) {
        if (arr[i] > maxVal) {
            maxVal = arr[i];
        }
    }
    return maxVal;
}

Pattern 3: Transformation Function

void transform(int arr[], int size, int (*operation)(int)) {
    for (int i = 0; i < size; i++) {
        arr[i] = operation(arr[i]);
    }
}

int square(int x) { return x * x; }
int doubleIt(int x) { return x * 2; }

// Usage
transform(numbers, 5, square);

Pattern 4: Filter/Copy Pattern

int filterPositive(const int src[], int srcSize, int dest[]) {
    int count = 0;
    for (int i = 0; i < srcSize; i++) {
        if (src[i] > 0) {
            dest[count++] = src[i];
        }
    }
    return count;  // Return new size
}

Pattern 5: Swap Function for Sorting

void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

void bubbleSort(int arr[], int size) {
    for (int i = 0; i < size - 1; i++) {
        for (int j = 0; j < size - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                swap(&arr[j], &arr[j + 1]);
            }
        }
    }
}

Best Practices

1. Always Pass Size

// GOOD
void process(int arr[], int size);

// BAD - How many elements?
void process(int arr[]);

2. Use const for Read-Only

// Document that array won't be modified
int sum(const int arr[], int size);
void print(const int arr[], int size);

3. Use Meaningful Parameter Names

// GOOD
void copyArray(const int source[], int dest[], int count);

// BAD
void copyArray(const int a[], int b[], int n);

4. Validate Parameters

int sum(const int arr[], int size) {
    if (arr == NULL || size <= 0) {
        return 0;
    }
    // ... rest of function
}

5. Use Macros for Size Calculation

#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))

int arr[] = {1, 2, 3, 4, 5};
process(arr, ARRAY_SIZE(arr));

6. Document Array Ownership

// Caller owns the array - function just reads
void analyze(const int data[], int size);

// Function modifies caller's array
void sort(int data[], int size);

// Function allocates - caller must free
int* createCopy(const int src[], int size);

Summary

Quick Reference

Array TypeFunction ParameterNotes
1D int arr[]int arr[] or int *arrPass size separately
2D int arr[][N]int arr[][N] or int (*arr)[N]Columns must be specified
Constconst int arr[]Prevents modification

Key Points

āœ“ Arrays decay to pointers when passed to functions
āœ“ Modifications affect the original array
āœ“ Size information is lost - always pass size
āœ“ Use const for read-only parameters
āœ“ Cannot return local arrays directly
āœ“ For 2D arrays, specify all dimensions except first

Function Declaration Equivalents

// These are ALL equivalent for 1D:
void f(int arr[]);
void f(int arr[100]);
void f(int *arr);

// These are ALL equivalent for 2D with 4 columns:
void f(int arr[][4]);
void f(int arr[3][4]);
void f(int (*arr)[4]);

Next Steps

After mastering passing arrays to functions:

  1. Study Arrays and Pointers relationship in depth
  2. Learn about Character Arrays (strings)
  3. Explore Dynamic Memory Allocation for flexible arrays

"Pass the address, not the data - that's how C handles arrays."

Skill Check

Test this lesson

Answer 4 quick questions to lock in the lesson and feed your adaptive practice queue.

--
Score
0/4
Answered
Not attempted
Status
1

Which module does this lesson belong to?

2

Which section is covered in this lesson content?

3

Which term is most central to this lesson?

4

What is the best way to use this lesson for real learning?

Your answers save locally first, then sync when account storage is available.
Practice queue