0

I am trying to understand pass by value and reference and I thought c was a language of pass by reference and tried to implement a swap function, that changes the values of two numbers to have values of each other, but the values don't seem to change at all. so can you please see the ff code and point out what I am doing wrong? in my thought, since we are assigning the pointer variables to have a value of another variable(which points to a certain number) it should have swapped the values but it's not what is happening(i.e. The values don't get swapped) so there must be some thing I am not understanding. would appreciate if you can help

#include <stdio.h>
void swap(int *a, int *b){
    int *temp;
    temp = a;
    a=b;
    b=temp;

}

int main()
{
    int a = 78;
    int b = 98;
    int *c=&a;
    int *d=&b;
    swap(c,d);
    //swap(&a,&b)
    printf(" a is %d and b is %d \n",*c,*d);

    return 0;
}
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
Solruhama
  • 101
  • 7

5 Answers5

4

C is always pass-by-value.

what you're looking for is

void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

This way you will be passing the addresses of the integers you want to swap, and the swap function will swap the values written in those address.

I Dav
  • 374
  • 1
  • 2
  • 8
1

I am trying to understand pass by value and reference and I thought c was a language of pass by reference

No, C is a language of pass by value.

C++ has reference parameters. In C++, you could have written something very close to your original, and it would have worked:

void swap(int &a, int &b){
    int temp;
    temp = a;
    a = b;
    b = temp;
}

Declarations like `` say that the parameter will be passed by reference. So when you say things like temp = a and a = b, you're actually affecting the caller's variable, as you want to. (The other change I made here is that temp is a plain int.)

But that was C++. In C, there are no reference parameters, and everything [note 1] is passed by value. But you can use pointers to explicitly simulate pass by reference. It looks like this:

void swap(int *a, int *b){
    int temp;
    temp = *a;
    *a = *b;
    *b = temp;
}

This is also close to what you originally wrote, with the exception that we are explicitly manipulating *a and *b. If a and b are pointers to variables in the caller, then we write *a and *b to say that we are manipulating what the pointer points to, that is, the variables in the caller.

In this case we must call

swap(&a, &b)

in the caller (as in fact you had in a comment).


[Note 1: Arguably, there is one place in C that does have pass by reference, and that's when you pass an array to a function. See more discussion at this question.]

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
  • I won't have the argument with you that your Note 1 invites, but as long as you're going to bring that up, passing functions has analogous semantics to passing arrays, so that makes *two* places. – John Bollinger Oct 02 '22 at 15:21
0

You are just swapping the local copies of your pointers, not the things the pointer point to, as you forgot to dereference your pointers. Also, your temp value holds a pointer too, which doesn't make sense - it should hold one of the values you are swapping.

Instead of this...

int *temp;
temp = a;
a = b;
b = temp;

...you need this:

int temp;
temp = *a;
*a = *b;
*b = temp;

For better understanding, let's explain this with an analogy.

Suppose your memory is a cabinet full of drawers. Each draw is labelled with some letter (its address), and in the drawers, there are pieces of paper with other numbers or letters written on them (the values stored at each address). In this explanation I use letters as addresses even though in reality, addresses are numbers as well.

Now let's see what happens in your original code. To simplify the explanation, I will inline the function.

This is what we have then:

int a = 78;
int b = 98;
int *c = &a;
int *d = &b;

int *temp = c;
c = d;
d = temp;

And this is what it does:

// Let's put a note with `78` written on it into drawer `A`.
int a = 78;
// Now we have: A="78"

// Let's put a note with `98` written on it into drawer `B`.
int b = 98;
// Now we have: A="78", B="98"

// Let's put a note with `A` written on it into drawer `C`.
int *c = &a;
// Now we have: A="78", B="98", C="A"

// Let's put a note with `B` written on it into drawer `D`.
int *d = &b;
// Now we have: A="78", B="98", C="A", D="B"

// Let's check the note in drawer `C` (which currently says `A`)
// and put a similar note into drawer `T`.
int *temp = c;
// Now we have: A="78", B="98", C="A", D="B", T="A"

// Let's check the note in drawer `D` (which currently says `B`)
// and put a similar note into drawer `C` (discarding what is
// already there).
c = d;
// Now we have: A="78", B="98", C="B", D="B", T="A"

// Let's check the note in drawer `T` (which currently says `A`)
// and put a similar note into drawer `D` (discarding what is
// already there).
d = temp;
// Now we have A="78", B="98", C="B", D="A", T="A"

If you look at the final result, you will see that the contents of the drawers A and B never changed. We just fiddled around a bit with our drawers C, D and T.

Now let's look at the working solution, again inlined for simplicity:

int a = 78;
int b = 98;
int *c = &a;
int *d = &b;

int temp = *c;
*c = *d;
*d = temp;

And see how it works now:

// Let's put a note with `78` written on it into drawer `A`.
int a = 78;
// Now we have: A="78"

// Let's put a note with `98` written on it into drawer `B`.
int b = 98;
// Now we have: A="78", B="98"

// Let's put a note with `A` written on it into drawer `C`.
int *c = &a;
// Now we have: A="78", B="98", C="A"

// Let's put a note with `B` written on it into drawer `D`.
int *d = &b;
// Now we have: A="78", B="98", C="A", D="B"

// ==== SO FAR SO GOOD, NOW LET'S SEE WHAT IS DIFFERENT THIS TIME ===

// Let's check the note in drawer `C` (which currently says `A`).
// Then LOOK INTO ANOTHER DRAWER according to the note we just found
// (which said `A`), look at the note in there (which currently says
// `78`) and put a similar note into drawer `T`.
int temp = *c;
// Now we have: A="78", B="98", C="A", D="B", T="78"

// Let's check the note in drawer `D` (which currently says `B`).
// Then LOOK INTO ANOTHER DRAWER according to the note we just found
// (which said `B`), look at the note in there (which currently says
// `98`) and prepare a similar note.
// Then check the note in drawer `T` (which currently says `A`).
// Then OPEN ANOTHER DRAWER according to the second note we just found
// (which said `A`), and put the copied note that we got before
// (which said `98`) into that drawer.
*c = *d;
// Now we have: A="98", B="98", C="A", D="B", T="78"

// Let's check the note in drawer `T` (which currently says `78`)
// and prepare a similar note.
// Then check the note in drawer `D` (which currently says `B`).
// Then OPEN ANOTHER DRAWER according to the second note we just found
// (which said `B`), and put the copied note that we got before
// (which said `78`) into that drawer.
*d = temp;
// Now we have A="98", B="78", C="A", D="B", T="78"

The difference is that this time we use C and D only to store a "pointer" to A and B, and not directly any numbers, so we always looked into C or D to find another drawer's letter in there and then continue accessing that drawer, so we ended up actually swapping the numbers in A and B as we wanted.

You may say, "why did we even have to use extra drawers C and D then?", and you would be right - but only because this example inlined the function call. If you call a function, you have to pass some "notes" (arguments) to it which it will put in its own drawers (argument variables) - you can't pass whole drawers (i.e. values by reference). So instead of being able to pass the drawers A and B directly, you do the next best thing and pass two notes with A and B written on them - your pointers - which the function will put into its own drawers C and D at that point. The function can then check what's written on these notes to know which actual drawers (with your numbers in them) you wanted the function to operate on.


By the way, C is pass-by-value, not pass-by-reference. C++ can be both because it has a reference type, but C doesn't. All you can do is use pointers, but then you are technically still passing a pointer by value, although logically speaking, you can view the pointer as reference to some value so you could say you are passing that value by reference, even though syntactically you aren't.

For comparison, in C++ you could do this:

// The & here is *not* related to pointers but means
// "pass this argument by reference", essentially
void swap (int &a, int &b) {
  int temp = a;
  a = b;
  b = temp;
}

// ...

int a = 123;
int b = 456;
swap(a, b);

In terms of the analogy above, this would be equivalent to passing the whole drawers A and B to the function so that the function can temporarily put them into its own cabinet and swap the notes inside of them, instead of passing notes with the labels of your own drawers.

CherryDT
  • 25,571
  • 5
  • 49
  • 74
0

In C the term pass by reference means passing an object indirectly through a pointer to it. So dereferencing the pointer you have a direct access to the object pointed to by the pointer.

Thus if you want to change values of the variables a and b within the function swap you need to dereference the passed pointers that point to the variables to get the access to them.

void swap(int *a, int *b){
    int temp = *a;
    *a = *b;
    *b = temp;
}

If you want to swap the pointers themselves to make the pointer c to point to the variable b and the pointer d to point to the variable a then again you need to pass them to the function by reference through pointers to them.

In this case the function will look the following way

void swap(int **a, int **b){
    int *temp = *a;
    *a = *b;
    *b = temp;
}

and can be called like

swap( &c, &d );
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
0

C is strictly pass-by-value - formal parameters in the function definition are always separate objects in memory that receive copies of the values from the actual parameters in the function call. To help make this clear I'm going to use different names for the formal and actual parameters:

void swap( int a, int b )
{
  int tmp = a;
  a = b;
  b = tmp;
}

int main( void )
{
  int x = 10, y = 20;
  printf( "Before swap: x = %d, y = %d\n", x, y );
  swap( x, y );
  printf( " After swap: x = %d, y = %d\n", x, y );
  return 0;
}

a and b are different objects in memory than x and y - when you call swap, the expressions x and y are fully evaluated, and the results of those evaluations (the integer values 10 and 20) are copied to a and b.

Any changes to a or b have no effect on x or y, so the output of the above program will be

Before swap: x = 10, y = 20
 After swap: x = 10, y = 20

To fake pass-by-reference semantics, we use pointers:

void swap( int *a, int *b )
{
  int tmp = *a;
  *a = *b;
  *b = tmp;
}

int main( void )
{
  int x = 10, y = 20;
  printf( "Before swap: x = %d, y = %d\n", x, y );
  swap( &x, &y );
  printf( " After swap: x = %d, y = %d\n", x, y );
  return 0;
}

a and b are still separate objects in memory from x and y, but instead receiving copies of of the values of x and y, they receive the results of the expressions &x and &y, or the addresses of x and y, giving us this set of conditions:

 a == &x // int * == int *
 b == &y // int * == int *

*a ==  x // int == int
*b ==  y // int == int

The expressions *a and *b can kinda-sorta-but-not-really be thought of as aliases for x and y - writing to *a in swap is the same as writing to x in main.

This time, the output would be

Before swap: x = 10, y = 20
 After swap: x = 20, y = 10
John Bode
  • 119,563
  • 19
  • 122
  • 198