Passing Arrays to Functions in C
Updated 9 min read Aimerscrew
You've written functions that take numbers, and you've built arrays that hold many numbers. Putting the two together is where C programs start to feel powerful: one function that averages any list of grades, another that finds the highest score. But passing arrays to functions in C works differently from passing a single int, and knowing why will save you from some confusing bugs. Let's dig in.
🌍 Real-life example
Imagine a teacher's grade book on a shared online spreadsheet. When the teacher asks an assistant to "add 5 bonus points to everyone", she doesn't photocopy the sheet and hand over the copy; she sends the link. The assistant edits the real sheet, and the teacher sees the changes. Passing an array to a function in C works like sending that link, along with a note saying how many rows there are.
How do you pass an array to a function in C?
Write the parameter with empty square brackets, and add a second parameter for the length. When you call the function, pass just the array's name, with no brackets:
#include <stdio.h>
int sum_array(const int values[], size_t length) {
int total = 0;
for (size_t i = 0; i < length; i++) {
total += values[i];
}
return total;
}
int main(void) {
int scores[] = {78, 92, 85, 64, 90};
size_t count = sizeof scores / sizeof scores[0];
printf("Total: %d\n", sum_array(scores, count));
return 0;
}
Total: 409
Inside sum_array, the parameter values behaves like the array: values[i] reads element i. The const keyword promises that the function only reads the array; more on that in a moment.
What does "an array decays to a pointer" mean?
C never copies a whole array into a function. Instead, when you write sum_array(scores, count), the array name scores turns into the address of its first element. This automatic conversion is called array decay, and a variable that holds an address is called a pointer, the topic of the next chapter.
So int values[] in a parameter list really means "the address of an int". These two function headers are exactly the same to the compiler:
int sum_array(const int values[], size_t length);
int sum_array(const int *values, size_t length); /* pointer form, coming soon */
Passing an address is fast, even for an array of a million elements: only one small address travels into the function, never the data itself.
Why must you pass the length of the array?
Because the function only receives an address, it has no idea where the array ends. The sizeof trick doesn't help inside the function:
void show_size(int values[]) {
printf("%zu\n", sizeof values); /* size of an address, not the array! */
}
On most 64-bit systems this prints 8 (the size of an address) no matter how big the array is, and GCC warns you: 'sizeof' on array function parameter 'values' will return size of 'int *'. The fix is the pattern you've already seen: compute the length with sizeof where the array is declared, then pass it in as a size_t parameter.
📝 Note
Strings are the exception that proves the rule. A function that takes a string usually skips the length parameter, because the '\0' at the end marks where the text stops. That's how strlen works.
Can a function change the caller's array?
Yes. With a plain int parameter, the function gets a copy, so changing it inside the function doesn't affect the caller (you saw this in function parameters). With an array, the function gets the address of the caller's elements, so it changes the originals:
#include <stdio.h>
void print_array(const int values[], size_t length) {
for (size_t i = 0; i < length; i++) {
if (i > 0) {
printf(", ");
}
printf("%d", values[i]);
}
printf("\n");
}
void add_bonus(int values[], size_t length, int bonus) {
for (size_t i = 0; i < length; i++) {
values[i] += bonus;
}
}
int main(void) {
int scores[] = {70, 85, 90};
size_t count = sizeof scores / sizeof scores[0];
printf("Before: ");
print_array(scores, count);
add_bonus(scores, count, 5);
printf("After: ");
print_array(scores, count);
return 0;
}
Before: 70, 85, 90
After: 75, 90, 95
This is often exactly what you want: functions that sort, fill, reset or update an array work this way. Since C can't return a whole array from a function, filling an array that the caller passes in is the standard way to "give back" many values.
Using const for read-only arrays
If a function should only read an array, mark the parameter const, as in print_array and sum_array above. It does two things:
- It tells anyone calling the function, "I won't modify your data".
- It makes the compiler stop you if you try. Writing
values[i] = 0;inside the function is a compile error:assignment of read-only location.
double average(const int values[], size_t length); /* reads only */
void reset_all(int values[], size_t length); /* will modify */
Just by reading the two headers, you know which function can change your array. That's a big help in larger programs.
Returning results from an array: sum, max and average
A function can't return an array, but it can easily return a single answer computed from one. Here's a small grade book that uses several focused functions:
#include <stdio.h>
double average(const int values[], size_t length) {
int total = 0;
for (size_t i = 0; i < length; i++) {
total += values[i];
}
return (double)total / length;
}
int highest(const int values[], size_t length) {
int best = values[0];
for (size_t i = 1; i < length; i++) {
if (values[i] > best) {
best = values[i];
}
}
return best;
}
int lowest(const int values[], size_t length) {
int worst = values[0];
for (size_t i = 1; i < length; i++) {
if (values[i] < worst) {
worst = values[i];
}
}
return worst;
}
int count_at_least(const int values[], size_t length, int minimum) {
int count = 0;
for (size_t i = 0; i < length; i++) {
if (values[i] >= minimum) {
count++;
}
}
return count;
}
int main(void) {
int grades[] = {88, 72, 95, 61, 79, 84};
size_t students = sizeof grades / sizeof grades[0];
printf("Students: %zu\n", students);
printf("Average: %.1f\n", average(grades, students));
printf("Highest: %d\n", highest(grades, students));
printf("Lowest: %d\n", lowest(grades, students));
printf("Scored 70 or more: %d\n", count_at_least(grades, students, 70));
return 0;
}
Students: 6
Average: 79.8
Highest: 95
Lowest: 61
Scored 70 or more: 5
The total is 479, and 479 / 6 is about 79.83, shown as 79.8. The cast (double)total makes the division keep its decimals, as you learned in type conversion. Each function does one job and works for any number of grades: next term, with 30 students, nothing changes but the array.
⚠️ Be careful
highest and lowest start from values[0], so they must never be called with a length of 0: there would be no first element to read. In real code, check if (length == 0) before calling them.
Passing strings and 2D arrays to functions
Strings travel the same way as any array, but the function can loop until '\0' instead of taking a length:
#include <stdio.h>
#include <string.h>
int count_vowels(const char text[]) {
int count = 0;
for (size_t i = 0; text[i] != '\0'; i++) {
if (strchr("aeiouAEIOU", text[i]) != NULL) {
count++;
}
}
return count;
}
int main(void) {
char phrase[] = "Hello World";
printf("\"%s\" has %d vowels\n", phrase, count_vowels(phrase));
return 0;
}
"Hello World" has 3 vowels
For a 2D array, the parameter must include the column count, because the function needs it to find where each row starts. Pass the row count separately:
#define DAYS 4
int grand_total(int sales[][DAYS], size_t stores);
Common mistakes when passing arrays to functions
1. Using sizeof on an array parameter
Inside the function, sizeof values / sizeof values[0] divides the size of an address by the size of an int, so on most 64-bit systems you get 2, whatever the real length. Always pass the length.
2. Writing brackets in the call
sum_array(scores[], count) is a syntax error, and sum_array(scores[5], count) passes a single (out-of-bounds) element. Pass just the name: sum_array(scores, count).
3. Expecting the array to be copied
If a function modifies its array parameter, the caller's array changes too. If you need the original kept safe, copy it into a second array first, or mark the parameter const so the compiler blocks changes.
4. Passing the wrong length
A length bigger than the real array makes the function read or write past the end, which is undefined behavior: garbage values or a crash. Compute the length with sizeof once, next to the array, and pass that variable.
5. Trying to return a local array
A function can't return an array, and returning the address of its own local array is a serious bug, because that memory disappears when the function ends. Let the caller pass in an array to fill instead. (You'll learn the other option, malloc, later.)
Practice exercise
🏋️ Try it yourself
Write a function void reverse_array(int values[], size_t length) that reverses an array in place (inside the same array), plus a print_array function. Test it on {1, 2, 3, 4, 5} and print the array before and after.
Show solution
#include <stdio.h>
void print_array(const int values[], size_t length) {
for (size_t i = 0; i < length; i++) {
if (i > 0) {
printf(", ");
}
printf("%d", values[i]);
}
printf("\n");
}
void reverse_array(int values[], size_t length) {
for (size_t i = 0; i < length / 2; i++) {
int temp = values[i];
values[i] = values[length - 1 - i];
values[length - 1 - i] = temp;
}
}
int main(void) {
int numbers[] = {1, 2, 3, 4, 5};
size_t count = sizeof numbers / sizeof numbers[0];
printf("Before: ");
print_array(numbers, count);
reverse_array(numbers, count);
printf("After: ");
print_array(numbers, count);
return 0;
}
Before: 1, 2, 3, 4, 5
After: 5, 4, 3, 2, 1
The loop swaps the first and last elements, then the second and second-to-last, and stops at the middle (length / 2). With 5 elements it swaps twice and leaves the middle one alone. reverse_array has no const because it must change the array.
What's next?
You've now met addresses twice: an array name decays into one, and strchr returns one. It's time to understand them properly. The next chapter starts with pointers in C, one of the most important ideas in the whole language.
Key takeaways
- When you pass an array to a function, C passes the address of its first element, not a copy (array decay).
- A function cannot find an array parameter's length with sizeof, so pass the length as a size_t parameter.
- Changes a function makes to array elements change the caller's original array.
- Mark array parameters const when the function only reads them; the compiler then blocks accidental writes.
- Functions cannot return arrays, but they can return single results like a sum, maximum or average.
- Strings can be passed without a length because '\0' marks the end; 2D array parameters need the column count.
Frequently asked questions
How do you pass an array to a function in C?
Declare the parameter as type name[], for example int values[], add a size_t length parameter, and call the function with just the array name: sum_array(scores, count).
Are arrays passed by reference in C?
Technically C passes everything by value, but for an array the value passed is the address of its first element. The effect is similar to pass by reference: the function works on the original elements.
Why does sizeof give the wrong size for an array parameter?
Inside the function, the array parameter is really a pointer, so sizeof returns the size of an address (usually 8 bytes on 64-bit systems), not the size of the array. Pass the length separately.
How do I stop a function from changing my array?
Declare the parameter as const, for example const int values[]. Any attempt to modify an element inside the function becomes a compile error.
Can a function return an array in C?
No. A function can return a single value computed from an array, or fill an array that the caller passes in. Dynamic memory with malloc is another option you will learn later.