Vectors in C are passed by reference, but C doesn't have a vector type

C does not have a built-in vector data type the way C++ does. When people ask whether vectors are passed by reference in C, they are usually asking about one of two things: how to pass arrays (which C treats as pointers), or how to use a vector library that mimics C++ behavior.

In standard C, arrays decay into pointers when you pass them to a function. This means the function receives the memory address of the first element, not a copy of the entire array. The function can modify the original array's contents because it is working directly with that memory location. This is effectively pass-by-reference behavior, even though C does not use the term "reference" the way C++ does.

If you are using a third-party vector library for C (such as those found in educational projects or open-source collections), the behavior depends entirely on how that library is written. Most vector libraries in C use structs that contain a pointer to dynamically allocated memory, and passing the struct itself is pass-by-value—but the pointer inside still points to the original data.

Key Takeaways

  • C has no native vector type; arrays automatically convert to pointers when passed to functions, allowing the function to modify the original array.
  • Passing an array name to a function passes the memory address, not a copy, so changes inside the function affect the original data.
  • If you use a vector library in C, passing a vector struct is pass-by-value, but the pointer member still references the original heap memory.
  • To explicitly pass by reference in C, you must use a pointer and dereference it inside the function.

How arrays decay to pointers in C

When you write a function that takes an array parameter, C automatically converts the array name into a pointer to its first element. This conversion happens at compile time and is called array decay.

For example, if you declare a function like this:

void modify_array(int arr[10]) { arr[0] = 99; }

The compiler treats arr as a pointer to the first element, equivalent to int *arr. When you call the function with an array name, you are passing the address of that array's first element. Inside the function, any changes you make to the array contents modify the original data in the caller's memory space.

This is why the size parameter in the function signature (the 10 in the example above) is ignored by the compiler. The function receives only the address; it has no way to know how many elements actually exist. You must pass the array size as a separate parameter if the function needs to know it.

The difference between passing the array name and passing a pointer to the array

There is a subtle but important distinction in C between passing an array name and passing a pointer to an array. When you pass the array name alone, it decays to a pointer to the first element. When you explicitly take the address of the array itself using &array, you get a pointer to the entire array as a single object.

In practice, for most purposes, these two approaches point to the same memory location. However, pointer arithmetic behaves differently. If you have an array of 10 integers and you increment a pointer to the first element, it moves forward by 4 bytes (on a typical system). If you increment a pointer to the entire array, it moves forward by 40 bytes (the size of all 10 integers).

For everyday C programming, you almost always want to pass the array name, which decays to a pointer to the first element. Passing &array is rarely necessary and can cause confusion.

Using vector libraries in C

Several open-source projects provide vector-like data structures for C, including libraries like glib (which includes GPtrArray and GArray) and smaller educational implementations. These libraries typically define a struct that holds a pointer to dynamically allocated memory, a count of elements, and a capacity.

When you pass such a struct to a function, the struct itself is passed by value—the function receives a copy of the struct. However, the pointer member inside that struct still points to the original heap memory. This means the function can read and modify the vector's contents, but it cannot change the struct's size or capacity fields in a way that affects the caller's copy.

If you need a vector library function to resize the vector or change its capacity, you typically pass a pointer to the vector struct itself, not the struct directly. This allows the function to modify the struct's metadata.

Why C lacks true references

C was designed in the 1970s with simplicity as a core goal. The language does not have a reference type because pointers were considered sufficient for the same purpose. A pointer is an explicit variable that holds a memory address, and you must dereference it with the * operator to access the value it points to.

C++ added references later as a convenience feature—a reference is like a pointer that automatically dereferences itself. References also cannot be null and cannot be reassigned to point to a different object. These guarantees make code safer in some cases, but they add complexity to the language.

In C, if you want pass-by-reference behavior, you use a pointer and dereference it explicitly. This makes the intent clear in the code: when you see *ptr, you know you are accessing memory through an address.

Practical example: modifying an array in a function

Here is a concrete example of how array pass-by-reference works in C:

#include <stdio.h> void double_values(int arr[], int size) {   for (int i = 0; i < size; i++) {     arr[i] = arr[i] * 2;   } } int main() {   int numbers[5] = {1, 2, 3, 4, 5};   double_values(numbers, 5);   printf("%d\n", numbers[0]); // prints 2   return 0; }

The function double_values receives the address of the first element of numbers. When it modifies arr[0], it is modifying the original array in main. After the function returns, numbers[0] contains 2, not 1.

Frequently Asked Questions

Can I prevent a function from modifying an array I pass to it?

Yes, use the const keyword in the function parameter. Write void read_array(const int arr[], int size) and the compiler will prevent the function from modifying the array contents. The array is still passed by reference (as a pointer), but the const qualifier restricts what the function can do with it.

What happens if I pass a pointer to an array instead of the array name itself?

If you pass &array instead of array, you get a pointer to the entire array, not a pointer to the first element. For most practical purposes, they point to the same memory location, but pointer arithmetic works differently. Stick with passing the array name unless you have a specific reason to do otherwise.

Do I need to use malloc to create a vector in C?

If you are using a vector library, the library handles memory allocation internally. If you are simulating a vector with a struct that holds a pointer, yes, you would use malloc to allocate the heap memory that the pointer references. The struct itself can live on the stack, but the data it points to must be dynamically allocated.

Is passing an array by reference slower than passing a single integer?

No. Passing an array by reference (as a pointer) always passes just the memory address, which is typically 8 bytes on a modern system. Passing a single integer also passes 8 bytes on a 64-bit system. The size of the array does not affect the cost of the function call.