257

If C does not support passing a variable by reference, why does this work?

#include <stdio.h>

void f(int *j) {
  (*j)++;
}

int main() {
  int i = 20;
  int *p = &i;
  f(p);
  printf("i = %d\n", i);

  return 0;
}

Output:

$ gcc -std=c99 test.c
$ a.exe
i = 21 
S.S. Anne
  • 15,171
  • 8
  • 38
  • 76
aks
  • 4,695
  • 8
  • 36
  • 37
  • 41
    Where in this code are you passing _reference_? – Atul Feb 27 '15 at 02:42
  • 23
    It should be noted that C doesn't have pass by reference, it can only be *emulated* using pointers. – Some programmer dude Mar 09 '16 at 13:05
  • 12
    The correct statement is "C does not support *implicitly* passing a variable by reference" -- you need to explicitly create a reference (with `&`) before calling the function and explicitly dereference it (with `*`) in the function. – Chris Dodd May 26 '17 at 18:49
  • 2
    Your code output exactly equals when call `f(&i);` this is a implementation of pass by reference, which is not exist purely in C.[C pass by reference](https://stackoverflow.com/a/20443785/7508077) – EsmaeelE Jan 09 '19 at 21:33
  • 3
    @Someprogrammerdude Passing a pointer is passing-by-reference. This seems to be one of those facts that "savvy" C programmers pride themselves on. Like they get a kick out of it. "Oh you might THINK C has pass-by-reference but no it's actually just the value of a memory address being passed harharhar". Passing by reference literally just means passing the memory address of where a variable is stored rather than the variable's value itself. That is what C allows, and it is pass-by-reference every time you pass a pointer, because a pointer is a *reference* to a variables memory location. – ICW Aug 21 '19 at 18:19
  • 4
    @YungGun Technically, the pointer is itself passed by value. – Some programmer dude Aug 22 '19 at 05:26
  • @Someprogrammerdude A reference passed by value is still a reference, technically and untechnically... It just happens to be stored in a variables value... When you use C++ style "Authentic pass by reference", the compiler uses the "reference" variable as an alias, why do we call that alias a "reference"? it makes no sense at all. – ICW Sep 15 '19 at 22:25
  • 1
    @Atul *j **is** a reference – Sam Ginrich Feb 25 '22 at 09:31

19 Answers19

358

Because you're passing the value of the pointer to the method and then dereferencing it to get the integer that is pointed to.

Nifle
  • 11,745
  • 10
  • 75
  • 100
tvanfosson
  • 524,688
  • 99
  • 697
  • 795
  • 4
    f(p); -> does this mean passing by value? **then dereferencing it to get the integer that is pointed to.** -> could you please give more explanation. – bapi Mar 04 '14 at 13:02
  • 4
    @bapi, dereferencing a pointer means to "get the value that this pointer is referencing to". – Rauni Lillemets Nov 11 '14 at 13:35
  • What we call for the method of calling the function that takes address of the variable instead of passing the pointer. Example: func1(int &a) . Is this not a call by reference? In this case reference is really taken and in case of pointer, we are still passing by value because we are passing pointer by value only. – Jon Wheelock Oct 10 '15 at 17:14
  • 2
    When using pointers the key fact is that **copy of the pointer** is passed into the function. The function then uses that pointer, not the original one. This is still pass-by-value, but it works. – Danijel Nov 18 '16 at 10:24
  • 3
    @Danijel It's possible to pass a pointer that is not a copy of anything to a function call. For example, calling the function `func`: `func(&A);` This would pass a pointer to A to the function without copying anything whatsoever. It is pass-by-value, but that value is a reference, so you are 'passing-by-reference` the variable A. No copying required. It's valid to say it's pass-by-reference. – ICW Sep 18 '19 at 18:27
  • Important: dereferenced pointer is a reference. Not clear, how it comes, that pointer is seen as something else but an address. – Sam Ginrich Feb 25 '22 at 08:42
194

That is not pass-by-reference, that is pass-by-value as others stated.

The C language is pass-by-value without exception. Passing a pointer as a parameter does not mean pass-by-reference.

The rule is the following:

A function is not able to change the actual parameters value.

(The above citation is actually from the book K&R)


Let's try to see the differences between scalar and pointer parameters of a function.

Scalar variables

This short program shows pass-by-value using a scalar variable. param is called the formal parameter and variable at function invocation is called actual parameter. Note incrementing param in the function does not change variable.

#include <stdio.h>

void function(int param) {
    printf("I've received value %d\n", param);
    param++;
}

int main(void) {
    int variable = 111;

    function(variable);
    printf("variable %d\m", variable);
    return 0;
}

The result is

I've received value 111
variable=111

Illusion of pass-by-reference

We change the piece of code slightly. param is a pointer now.

#include <stdio.h>

void function2(int *param) {
    printf("I've received value %d\n", *param);
    (*param)++;
}

int main(void) {
    int variable = 111;

    function2(&variable);
    printf("variable %d\n", variable);
    return 0;
}

The result is

I've received value 111
variable=112

That makes you believe that the parameter was passed by reference. It was not. It was passed by value, the param value being an address. The int type value was incremented, and that is the side effect that make us think that it was a pass-by-reference function call.

Pointers - passed-by-value

How can we show/prove that fact? Well, maybe we can try the first example of Scalar variables, but instead of scalar we use addresses (pointers). Let's see if that can help.

#include <stdio.h>

void function2(int *param) {
    printf("address param is pointing to %d\n", param);
    param = NULL;
}

int main(void) {
    int variable = 111;
    int *ptr = &variable;

    function2(ptr);
    printf("address ptr is pointing to %d\n", ptr);
    return 0;
}

The result will be that the two addresses are equal (don't worry about the exact value).

Example result:

address param is pointing to -1846583468
address ptr   is pointing to -1846583468

In my opinion this proves clearly that pointers are passed-by-value. Otherwise ptr would be NULL after function invocation.

Ely
  • 10,860
  • 4
  • 43
  • 64
  • 2
    "ptr's address" is not correct: you print the pointer, which is the address of variable, not it's address, that would have been ´printf("ptr's address %d\n", &ptr);´ . The technical layer of passing a reference is passing a pointer, not an illusion! In function2, *param actually is a reference. – Sam Ginrich Feb 25 '22 at 08:24
  • Correct. What is demonstrated in the part "Pointers - passed-by-value" should actually be rephrased to: "printf("where pointer param is pointing to %d\n", param);" and "printf("where pointer ptr is pointing to %d\n", ptr);". Well caught^^ And I think what was intended to be shown is indeed: "printf("pointer params address %d\n", &param);" and "printf("pointer ptrs address %d\n", &ptr);" – Dan Apr 04 '22 at 16:59
  • Yes, your are right. I correct that. Indeed I wanted to show that the addresses they are pointing to are the same. The addresses of the pointers are not really interesting here (and are not the same). – Ely Apr 04 '22 at 19:18
77

In C, Pass-by-reference is simulated by passing the address of a variable (a pointer) and dereferencing that address within the function to read or write the actual variable. This will be referred to as "C style pass-by-reference."

Source: www-cs-students.stanford.edu

Tamas Czinege
  • 118,853
  • 40
  • 150
  • 176
59

Because there is no pass-by-reference in the above code. Using pointers (such as void func(int* p)) is pass-by-address. This is pass-by-reference in C++ (won't work in C):

void func(int& ref) {ref = 4;}

...
int a;
func(a);
// a is 4 now
Alexander Gessler
  • 45,603
  • 7
  • 82
  • 122
32

Your example works because you are passing the address of your variable to a function that manipulates its value with the dereference operator.

While C does not support reference data types, you can still simulate passing-by-reference by explicitly passing pointer values, as in your example.

The C++ reference data type is less powerful but considered safer than the pointer type inherited from C. This would be your example, adapted to use C++ references:

void f(int &j) {
  j++;
}

int main() {
  int i = 20;
  f(i);
  printf("i = %d\n", i);

  return 0;
}
Daniel Vassallo
  • 337,827
  • 72
  • 505
  • 443
  • 5
    That Wikipedia article is about C++, not C. References existed before C++ and don't depend on special C++ syntax to exist. –  Feb 10 '10 at 03:52
  • 2
    @Roger: Good point... I removed explicit reference to C++ from my answer. – Daniel Vassallo Feb 10 '10 at 08:44
  • 1
    And that new article says "a reference is often called a pointer" which isn't quite what your answer says. –  Feb 10 '10 at 16:13
14

You're passing a pointer(address location) by value.

It's like saying "here's the place with the data I want you to update."

antony.trupe
  • 10,640
  • 10
  • 57
  • 84
8

No pass-by-reference in C, but p "refers" to i, and you pass p by value.

Pavel Radzivilovsky
  • 18,794
  • 5
  • 57
  • 67
7

p is a pointer variable. Its value is the address of i. When you call f, you pass the value of p, which is the address of i.

Anssi
  • 518
  • 4
  • 8
7

In C everything is pass-by-value. The use of pointers gives us the illusion that we are passing by reference because the value of the variable changes. However, if you were to print out the address of the pointer variable, you will see that it doesn't get affected. A copy of the value of the address is passed-in to the function. Below is a snippet illustrating that.

void add_number(int *a) {
    *a = *a + 2;
}

int main(int argc, char *argv[]) {
   int a = 2;

   printf("before pass by reference, a == %i\n", a);
   add_number(&a);
   printf("after  pass by reference, a == %i\n", a);

   printf("before pass by reference, a == %p\n", &a);
   add_number(&a);
   printf("after  pass by reference, a == %p\n", &a);

}

before pass by reference, a == 2
after  pass by reference, a == 4
before pass by reference, a == 0x7fff5cf417ec
after  pass by reference, a == 0x7fff5cf417ec
Arun Suresh
  • 71
  • 1
  • 3
6

Short answer: Yes, C does implement parameter passing by reference using pointers.

While implementing parameter passing, designers of programming languages use three different strategies (or semantic models): transfer data to the subprogram, receive data from the subprogram, or do both. These models are commonly known as in mode, out mode, and inout mode, correspondingly.

Several models have been devised by language designers to implement these three elementary parameter passing strategies:

Pass-by-Value (in mode semantics) Pass-by-Result (out mode semantics) Pass-by-Value-Result (inout mode semantics) Pass-by-Reference (inout mode semantics) Pass-by-Name (inout mode semantics)

Pass-by-reference is the second technique for inout-mode parameter passing. Instead of copying data back and forth between the main routine and the subprogram, the runtime system sends a direct access path to the data for the subprogram. In this strategy the subprogram has direct access to the data effectively sharing the data with the main routine. The main advantage with this technique is that its absolutely efficient in time and space because there is no need to duplicate space and there is no data copying operations.

Parameter passing implementation in C: C implements pass-by-value and also pass-by-reference (inout mode) semantics using pointers as parameters. The pointer is send to the subprogram and no actual data is copied at all. However, because a pointer is an access path to the data of the main routine, the subprogram may change the data in the main routine. C adopted this method from ALGOL68.

Parameter passing implementation in C++: C++ also implements pass-by-reference (inout mode) semantics using pointers and also using a special kind of pointer, called reference type. Reference type pointers are implicitly dereferenced inside the subprogram but their semantics are also pass-by-reference.

So the key concept here is that pass-by-reference implements an access path to the data instead of copying the data into the subprogram. Data access paths can be explicitly dereferenced pointers or auto dereferenced pointers (reference type).

For more info please refer to the book Concepts of Programming Languages by Robert Sebesta, 10th Ed., Chapter 9.

5

In C, to pass by reference you use the address-of operator & which should be used against a variable, but in your case, since you have used the pointer variable p, you do not need to prefix it with the address-of operator. It would have been true if you used &i as the parameter: f(&i).

You can also add this, to dereference p and see how that value matches i:

printf("p=%d \n",*p);
t0mm13b
  • 34,087
  • 8
  • 78
  • 110
  • Why did you feel the need to repeat all of the code (including that comment block..) to tell him he should add a printf? –  Feb 10 '10 at 03:41
  • @Neil: That error was introduced by @William's edit, I'll reverse it now. And now it's apparent that tommieb is only mostly right: you can apply & to any object, not just variables. –  Feb 10 '10 at 05:07
4

Because you're passing a pointer(memory address) to the variable p into the function f. In other words you are passing a pointer not a reference.

dave
  • 12,406
  • 10
  • 42
  • 59
4

You're not passing an int by reference, you're passing a pointer-to-an-int by value. Different syntax, same meaning.

xan
  • 7,511
  • 2
  • 32
  • 45
  • 1
    +1 "Different syntax, same meaning." .. So the same thing, as the meaning matters more than the syntax. –  Feb 10 '10 at 03:51
  • Not true. By calling `void func(int* ptr){ *ptr=111; int newValue=500; ptr = &newvalue }` with `int main(){ int value=0; func(&value); printf("%i\n",value); return 0; }` , it prints 111 instead of 500. If you are passing by reference, it should print 500. C does not support passing parameter by reference. – Konfle Dolex Feb 24 '13 at 04:39
  • @Konfle, if you are syntactically passing by reference, `ptr = &newvalue` would be disallowed. Regardless of the difference, I think you are pointing out that "same meaning" is not _exactly_ true because you also have extra functionality in C (the ability to reassign the "reference" itself). – xan Feb 24 '13 at 15:21
  • We never write something like `ptr=&newvalue` if it is passed by reference. Instead, we write `ptr=newvalue` Here is an example in C++: `void func(int& ptr){ ptr=111; int newValue=500; ptr = newValue; }` The value of the parameter passed into func() will become `500`. – Konfle Dolex Feb 24 '13 at 17:58
  • In the case of my comment above, it is pointless to pass param by reference. However, if the param is an object instead of POD, this will make a significant difference because any changed after `param = new Class()` inside a function would have no effect for the caller if it is passed by value(pointer). If `param` is passed by reference, the changes would be visible for the caller. – Konfle Dolex Feb 24 '13 at 18:05
2

pointers and references are two different thigngs.

A couple of things I have not seen mentioned.

A pointer is the address of something. A pointer can be stored and copied like any other variable. It thus have a size.

A reference should be seen as an ALIAS of something. It does not have a size and cannot be stored. It MUST reference something, ie. it cannot be null or changed. Well, sometimes the compiler needs to store the reference as a pointer, but that is an implementation detail.

With references you don't have the issues with pointers, like ownership handling, null checking, de-referencing on use.

  • This is not correct. The sizeof operator applied to a reference delivers the size of the type and the address operator "&" applied to a reference delivers the pointer, which can be stored and this is what technically happens, when parameters are passed. – Sam Ginrich Feb 24 '22 at 17:16
  • This is absolutely correct! The confusion on this term comes from languages like Java and JavaScript using this word to mean pointer which is a different use of the word than in Pass-by-Reference. – Sanjeev Apr 03 '23 at 15:13
1

What you are doing is pass by value not pass by reference. Because you are sending the value of a variable 'p' to the function 'f' (in main as f(p);)

The same program in C with pass by reference will look like,(!!!this program gives 2 errors as pass by reference is not supported in C)

#include <stdio.h>

void f(int &j) {    //j is reference variable to i same as int &j = i
  j++;
}

int main() {
  int i = 20;
  f(i);
  printf("i = %d\n", i);

  return 0;
}

Output:-

3:12: error: expected ';', ',' or ')' before '&' token
             void f(int &j);
                        ^
9:3:  warning: implicit declaration of function 'f'
               f(a);
               ^
Sunay
  • 11
  • 3
  • You are aware, that historic implementations of C++ compilers were actually language converters, they would take your script into C in a well defined way and guess what: a 'void f(int &j)' would become a 'void f(int *j)' and the parameter access 'j' a '*j', both references in C++, where the term 'reference' actually is defined. The fact, that a C compiler can't understand the C++-reference syntax will not undo the strict semantic equivalence of reference and de-referenced pointer. – Sam Ginrich Dec 08 '22 at 22:04
1

Calling a pointer a reference (as Java and Javascript do) is a completely different use of the word reference than in pass-by-reference. C does not support pass-by-reference. Here is your example re-written to show that it not really passing a value by reference, only a pointer by value.

#include <stdio.h>

void f(int *j) {
  int k = (*j) + 1;
  j = &k;
}

int main() {
  int i = 20;
  int *p = &i;
  f(p);
  printf("i = %d\n", i);
  printf("j = %d\n", *p);


  printf("i(ptr) = %p\n", &i);
  printf("j(ptr) = %p\n", p);


  return 0;
}

Here is the output

i = 20
j = 20
i(ptr) = 0x7ffdfddeee1c
j(ptr) = 0x7ffdfddeee1c

As you can see, the value stays the same, but more importantly the pointers don't change either. However, C++ allows pass by reference. Here is the same example put through a C++ compiler but with and added ampersand in the header which makes this a reference parameter.

#include <stdio.h>

void f(int *&j) {   // note the & makes this a reference parameter.
                    // can't be done in C
  int k = (*j) + 1;
  j = &k;
}

int main() {
  int i = 20;
  int *p = &i;
  f(p);
  printf("i = %d\n", i);
  printf("j = %d\n", *p);


  printf("i(ptr) = %p\n", &i);
  printf("j(ptr) = %p\n", p);


  return 0;
}

Here is the output

i = 20
j = 21
i(ptr) = 0x7ffcb8fc13fc
j(ptr) = 0x7ffcb8fc13d4

Note that we were able to change the actual pointer!

A great reference on this topic is The Dragon Book -- a classic Computer Science text book on compilers. Since, it's the most widely used text book on compilers (or at least it was when I was in college), I would guess that the vast, vast majority of people who design languages or write compilers leaned from this book. Chapter 1 of this book explains these concepts very clearly and explains why C is pass-by-value only.

Sanjeev
  • 1,517
  • 1
  • 18
  • 30
0

That code fragment (with tiny modification)

void add_number(int * const a) {
    *a = *a + 2;
}

also exists in C++ and is semantically equivalent to

void add_number(int &a) {
    a = a + 2;
}

The compiler is expected to generate equal binary code of the function add_number in both cases. Now, when you consider an integer to be a value, that value is passed by it's reference, where in the upper pattern the reference appears technically as pointer.

Conclusion
C supports the semantics of passing an instance by reference.
Even technically with int *a you pass *a, which is a reference.

Sam Ginrich
  • 661
  • 6
  • 7
-2

'Pass by reference' (by using pointers) has been in C from the beginning. Why do you think it's not?

Maurits Rijk
  • 9,789
  • 2
  • 36
  • 53
  • 9
    Because it's technically not passing by reference. – Mehrdad Afshari Feb 09 '10 at 14:32
  • 10
    Passing a pointer value is not the same as pass-by-reference. Updating the value of `j` (*not* `*j`) in `f()` has no effect on `i` in `main()`. – John Bode Feb 09 '10 at 14:34
  • 7
    It *is semantically the same thing* as passing by reference, and that's good enough to say it is passing by reference. True, the C Standard does not use the term "reference", but that neither surprises me nor is a problem. We are also not speaking standardese on SO, even though we may refer to the standard, otherwise we'd see no one talking about rvalues (the C Standard doesn't use the term). –  Feb 10 '10 at 03:45
  • 2
    @SamGinrich, "One aspect of C functions may be unfamiliar to programmers who are used to some other languages, particularly Fortran. In C, all function arguments are passed 'by value'. ..." -- Brian Kernighan (the creator of C) and Dennis Ritchie, "The C Programming Language, Second edition", Section 1.8. The first edition was written the mid 70's, almost 20 years before Java existed. This was the first C book ever written by the people who created C meaning they were there, at the beginning of C. – Sanjeev Feb 25 '22 at 20:30
  • @Sanjeev Then came Stroustrup, **defined** references, which share objects of calling and called context, and in retrospective Kernighan & Rithchie write "The way to obtain the desired effect is for the calling program to pass pointer to the **values** to be changed" – Sam Ginrich Feb 26 '22 at 22:16
  • @SamGinrich do you have a point? – Sanjeev Feb 26 '22 at 23:14
  • @Sanjeev The point is, that references appear on the stack as pointers, so we do have both. A reference on lower technical level is a pointer. Meta: Computer Science begins, where you stop clamping off semantics and it ends, where you cite an ox (or many of them), who you assume to be unfailing. – Sam Ginrich Feb 27 '22 at 10:14
  • 1
    @SamGinrich Yes, it's possible to implement PBR as a pointer to a pointer, but not necessary. There are other techniques for doing this. Anyway, if you're talking about the implementation of PBR, you're completely missing the point. It's really about the language specifications and the behavior when a programmer choses to use the PBR option. The implementation details under the hood are irrelevant. – Sanjeev Feb 28 '22 at 14:35
  • "it's possible to implement PBR as a pointer to a pointer" - why would one do this? – Sam Ginrich Feb 28 '22 at 16:19
  • @SamGinrich I found my other Compliers text book from college online for free. It Holub's "Complier Design in C". It's a really great book on compilers. Appendix B start on page 802 will answer your question. I have spent enough time on this. After this I'm done. – Sanjeev Feb 28 '22 at 21:51
  • 1
    @SamGinrich Oops. Forgot to post the link https://holub.com/goodies/compiler/compilerDesignInC.pdf – Sanjeev Mar 01 '22 at 01:56
  • @Sanjeev I would prefer you to understand my last question, kind of Q&U instead of Q&A. – Sam Ginrich Mar 01 '22 at 10:38
-2

I think C in fact supports pass by reference.

Most languages require syntactic sugar to pass by reference instead of value. (C++ for example requires & in the parameter declaration).

C also requires syntactic sugar for this. It's * in the parameter type declaration and & on the argument. So * and & is the C syntax for pass by reference.

One could now argue that real pass by reference should only require syntax on the parameter declaration, not on the argument side.

But now comes C# which does support by reference passing and requires syntactic sugar on both parameter and argument sides.

The argument that C has no by-ref passing cause the the syntactic elements to express it exhibit the underlying technical implementation is not an argument at all, as this applies more or less to all implementations.

The only remaining argument is that passing by ref in C is not a monolithic feature but combines two existing features. (Take ref of argument by &, expect ref to type by *.) C# for example does require two syntactic elements, but they can't be used without each other.

This is obviously a dangerous argument, cause lots of other features in languages are composed of other features. (like string support in C++)

Johannes
  • 665
  • 5
  • 10