Using array with C-Style Functions and Comparing with Other Containers
In that style, the parameter is declared as a pointer rather than as an array.
A Hand-Off Question to Predict
Sometimes a program has a fixed-size array and needs to give it to a function that was written in the older C style. In that style, the parameter is declared as a pointer rather than as an array. The central rule is that an array can be explicitly passed to a C-style function that expects a pointer. The array does not have to stay in the calling code. It can be handed to a function whose parameter is a pointer, and that function can work with the elements through the pointer.
What do you think happens?
A caller creates an array of four ints, {1, 2, 3, 4}, and passes it to a function that doubles each element through a pointer. What does the caller print afterwards?
Reveal answer
2 4 6 8, because the function changed the caller's own elements
The array is never copied. The function receives a pointer to the first element, and its writes go straight to the caller's array.
The rest of this article follows one question: when you make that hand-off, what does the function actually receive, and what does it lose along the way? Answering it is what makes array-to-pointer passing predictable instead of mysterious.
What Crosses the Function Boundary
Start with a concrete setup. A caller declares an array of four integers, then calls a function whose parameter is a pointer to int. Read the path step by step. The array is named in the call. At the point of the call, the name of the array is converted into a pointer to its first element. That pointer is what travels into the function's parameter.
The function never receives the whole array as one value. It receives a single pointer that refers to where the array begins. The array itself stays in the caller. What crosses the boundary is only the pointer to its first element. This is why the array is described as being passed to a function that expects a pointer: the expected argument type is the pointer, and the array name supplies it.
Reaching the Caller's Elements
Once the function has the pointer, it can reach each element by moving through the array from that starting point. The first element is at the position the pointer marks, and the others follow it. Using an index such as data[i] means 'the element i positions after the starting point.' Because the pointer leads back to the caller's own array, the function can read and change those elements. A change made through the pointer is visible to the caller once the function returns. The array was never copied.
The following example is original and matches the rule above. The function takes a pointer to int and a count. The caller passes the array name, which becomes the pointer, and passes the number of elements separately, because the pointer alone cannot provide it.
#include <stdio.h>
void double_all(int *data, int count) {
for (int i = 0; i < count; i++) {
data[i] = data[i] * 2;
}
}
int main(void) {
int values[4] = {1, 2, 3, 4};
double_all(values, 4);
for (int i = 0; i < 4; i++) {
printf("%d ", values[i]);
}
printf("\n");
return 0;
}- 1void
double_all(int *data, int count)declares a function that expects a pointer to int plus a separate count. - 2The loop runs count times. Each data[i] refers to an element of the caller's array, not to a copy.
- 3In main, values is an array of four ints. Naming it in the call hands over a pointer to values[0].
- 4The call
double_all(values, 4)passes that pointer and the count 4. - 5After the function returns, the loop in main prints 2 4 6 8, because the changes were made to the original elements.
- The parameter int *data is a pointer to int, the expected argument type.
- The name values in the call becomes a pointer to values[0].
- The count 4 travels separately because the pointer carries no length.
2 4 6 8What the Pointer Does Not Carry
The pointer gives the function access to the elements, but it does not carry the array length. The caller knew that the array had four elements. Once the array name has become a pointer, the function receives only the location of the first element. It has no built-in way to ask where the array ends. This is why the length must be passed separately, as the count parameter does in the example above.
Mistakes When Passing Arrays
Common mistake Passing the array to a pointer parameter without passing its length
What it looks likevoid double_all(int *data) with a loop that runs while the function guesses an end point, and the caller passing only valuesWhy it failsThe pointer carries no length, so the function has nothing reliable to stop at.
Do this insteadPass the number of elements as its own argument, such as double_all(values, 4), and use that count in the loop.
Common mistake Passing a count that does not match the array
What it looks likeint values[4] = {1, 2, 3, 4}; double_all(values, 6);Why it failsThe function trusts the count it is given. With 6, it reaches past the four elements the caller actually declared.
Do this insteadKeep the count tied to the array's real number of elements, and pass the same value the caller used when declaring the array.
Common mistake Expecting the function to work on a copy
What it looks likeCalling double_all(values, 4) and then printing values, expecting the original numbers.
Why it failsThe array is not copied. The function's writes change the caller's elements.
Do this insteadExpect changes made through the pointer to be visible to the caller after the call returns, and use this deliberately.
Comparing With Containers That Carry Their Own Size
The contrast becomes clearer when a raw array passed as a pointer is compared with a container object. A raw array handed to a pointer parameter gives the function a location and nothing more, so the size must travel alongside it. Some container types instead keep their size together with their data, so the object itself can answer how many elements it holds. The source pack for this article does not walk through the details of any specific container type, so treat the comparison below as a conceptual contrast rather than a full lesson on those types.
| Feature | Array passed to a pointer parameter | Container that stores its own size |
|---|---|---|
| What the function receives | A pointer to the first element | The container object, holding its data together |
| Length information | Not carried by the pointer; must be passed separately | Stored with the data |
| Access to caller's elements | Yes, through the pointer; writes are visible to the caller | Depends on how the container is passed; this source pack does not cover it |
| Where the size comes from | The programmer passes a count | The container itself |
A conceptual contrast between a raw array handed to a pointer parameter and a container that stores its size with its data.
Trying the Hand-Off Yourself
Write a function scale_all that takes a pointer to int and a count, and multiplies each element by 3. Call it from main on an array {1, 2, 3, 4, 5} and print the result. Predict the output before you run the code.
Hints
- Your parameter list should have the pointer first, then the count, mirroring
double_all. - Inside the loop, use data[i] on the left and right side of the assignment.
- Remember that the caller's array changes, so the printed values reflect the multiplication.
Key Takeaways
- A C-style function with a pointer parameter can receive an array: the array name becomes a pointer to its first element, and that pointer is what the function receives.
- The array is not copied. Through the pointer, the function works on the caller's own elements, so writes are visible after the call returns.
- The pointer does not carry the array length, so the length must be passed as a separate argument.
- Passing a count that does not match the real array, or omitting the count, are common mistakes that the programmer must prevent.
- Containers that store their size with their data answer the length question themselves, while a raw pointer does not.