Docs
Passing Arrays to Functions
Passing Arrays to Functions in C
Table of Contents
- ā¢Introduction
- ā¢How Arrays are Passed
- ā¢Passing 1D Arrays
- ā¢Passing 2D Arrays
- ā¢Passing Arrays by Reference
- ā¢Array Size in Functions
- ā¢Returning Arrays from Functions
- ā¢Common Patterns
- ā¢Best Practices
- ā¢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
| Aspect | Regular Variables | Arrays |
|---|---|---|
| Passed by | Value (copy) | Reference (address) |
| Changes in function | Don't affect original | Affect original |
| Memory overhead | Copy of data | Only address (8 bytes) |
| Size info preserved | Yes | No (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;
}
Method 2: Use Static Array (Not Recommended)
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 Type | Function Parameter | Notes |
|---|---|---|
1D int arr[] | int arr[] or int *arr | Pass size separately |
2D int arr[][N] | int arr[][N] or int (*arr)[N] | Columns must be specified |
| Const | const 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:
- ā¢Study Arrays and Pointers relationship in depth
- ā¢Learn about Character Arrays (strings)
- ā¢Explore Dynamic Memory Allocation for flexible arrays
"Pass the address, not the data - that's how C handles arrays."