The earlier answers given seem to be missing context, and don't address the property that C pointers are overloaded. It's completely fair to claim that C "passes by reference." A pointer is a reference, too. However, more formally, a C "reference" is the mechanism to represent a symbol's memory address.
- A pointer is simply a variable in memory whose value is treated as an
address.
- A reference is simply the value-literal for a memory address.
- The C language allows for passing by reference - but only in the right context!
The separation of context is that of signature definition and signature invocation.
Consider the primitives int a = 0x22e8;
and somewhere else we declare int* b = &a;
(I'll get to functions in a minute).
We could visualize this in memory (assume endianness doesn't matter for this example):
...
0x00000200: 22e8 ; this is the variable for int a's literal value:
; a == 0x000022e8
; &a == 0x00000200
0x00000???: 9090 ; ... more code ...
0x00000400: 0200 ; this is pointer b pointing to a's address:
; b == 0x00000200
; *b == 0x000022e8
; &b == 0x00000400
...
It's easy to see that we can assign a pointer to any address without any special tricks. A pointer is just a variable, but C allows us to reference the address it's pointing to; we can "dereference" the pointer from its value-address to instead treat the pointer as its pointee's underlying value during an invocation context: while ( *b == a ) ...
. We could easily invoke b == &a
.
When applying this to function calls, you have to separate the context of function (signature) definition vs invocation in order to differentiate pass by reference.
int* foo(int* blah, int nah) { .. } // signature definition context:
// These are parameters
... vs ...
b = foo( &a, 4); // invocation context:
// These are arguments
In defining a function signature, we are not telling the compiler which address an argument is accessed from — the runtime doesn't even know yet! It's just nonsense to define void bar(int &blah) {...}
. Instead we use the dereferenced pointer syntax — "whatever argument is passed in will be loaded at the address pointed to" — to reference the desired argument value when runtime occurs. Thus C can pass arguments by reference.
The contexts of both function signature definitions and function calls change how the compiler looks at pointer overloading vs reference, and how the runtime can actually address them.