Passing by reference in C

Viewed 464553

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 
19 Answers

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

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

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

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;
}

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."

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

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.

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);

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.

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

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);
               ^

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.

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!

Just as a reference The Dragon Book is a classic Computer Science text book on compilers. Because it's the most popular compiler book ever (or at least it was when I was in college, maybe I'm wrong), 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.

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

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.

Related