Swap call by reference confusion

Hello,
This is very silly question as millions discussions on call-by-value vs call-by-reference for programming beginners, but I need to confirm my understanding.

#include<stdio.h>

void swap(int *p, int *q)  //Line 3
{
    int tmp;
    tmp = *p; 
    *p = *q; 
    *q = tmp;
}

int main()
{
    int i = 3, j = 5;
    int *iptr, *jptr;
    iptr = &i; 
    jptr = &j; 
    printf("Before swap: i = %d, j = %d\n", i, j); 

    swap(iptr, jptr);   //Line 19
    printf("After swap: i = %d, j = %d\n", i, j); 

    return 0;
}

I know swap(*iptr, *jptr) is wrong when I tried it,

$ gcc -o pg252b  pg252_call-by_ref01b.c
pg252_call-by_ref01b.c: In function �main�:
pg252_call-by_ref01b.c:19:5: warning: passing argument 1 of �swap� makes  pointer from integer without a cast [enabled by default]
pg252_call-by_ref01b.c:3:6: note: expected �int *� but argument is of type �int�
pg252_call-by_ref01b.c:19:5: warning: passing argument 2 of �swap� makes  pointer from integer without a cast [enabled by default]
pg252_call-by_ref01b.c:3:6: note: expected �int *� but argument is of type �int�

My question is, as the function prototype at Line 3 is swap(int *i, int *j), in which the two arguments are pointers, whereas Line 19 passes two pointer addresses, which are "NOT" matching. Initially I tend to use swap(*iptr, *jptr) instead of swap (iptr, jptr), but not working. I did not see people ask why the function call uses addresses but the prototype declares with pointers. This gave me confusion. Could you guys explain it along with the warning message? Thanks a lot!

Why would int * not match int *?

There's no such distinction between "pointers" and "pointer addresses", I don't know where you got this idea -- could you illustrate your thinking further?

There is a distinction between constant and non-constant pointers, but they're both still pointers -- the difference is what the compiler will allow you to do with them. For instance,

int a[32];
a++;

You're not allowed to modify the base address of an array the same way you could an 'int *' variable, but 'a' is still a pointer when used in a statement.

If there is no such distinction, how come swap(*iptr, *jptr) does not work when I used it for Line 19?
My bad to use this expression

Here is a reference: On page 253 of the book "A book on C, by Al Kelly, Ira Pohl 4th Edition":

The effect of "call-by-reference" is accomplished by:
1) Declaring a function parameter to be a pointer;
2) Using the dereferenced pointer in the function body;
3) Passing an address as an argument when the function is called;

To me this is another rule of C that "This is the way to do the job"---not following logic. But, I know it is related to the pointer manipulation again.
My thinking is the "appearance" of the function prototype swap(int *, int *) and the call of the function is swap(&i, &j) that look "not" matching in form.

(iptr) is already an 'int *'. Putting a * in front of it dereferences it. This converts it back into an int.

It might look funny to you but is entirely correct.

Just think about what & and * do as operators. & converts a variable into an address, i.e. converts an "int" into an "int *.

  • converts an address into a variable, i.e. converts an "int *" into an "int".
1 Like

No, it does not look funny, but my thinking. I must have missed important things with pointers in C! And I will post more silly questions here for help. Thank you very much!

Let me try explaining it a different way:
The function declaration for swap:

void swap(int *p, int *q)
{ ... }

says that swap() takes two arguments of type pointer to int and does not return a value. So with the declarations:

    int i = 3, j = 5;
    int *iptr, *jptr;

and the statements:

    iptr = &i; 
    jptr = &j;

valid calls to swap() would include:

    swap(&i, &j);
      and
    swap(iptr, jptr);

because &i , &j , iptr , and jptr are all of type pointer to int. The call:

    swap(*iptr, *jptr);

is not valid because *iptr and *jptr (which can be read as "the int pointed to by the pointers to int iptr and jptr , respectively) are objects of type int; not objects of type pointer to int.

1 Like

Thanks Don!
While reading the post I was still thinking: about call-by-reference of previous post.

I think I have to remember these rules. Declare with pointers but call with address. Here "pointer" and "address" are the same in fact, just look different in form, and remember to add the "&" symbol for non-pointer variables, or without "*" for pointers including function/struct.

I've used the two terms interchangably since they do the same thing, in the end.

I suppose you could say a pointer, is a kind of variable which stores addresses.

You are correct that you don't have to dereference a function pointer, but they're special for a reason. Functions are addresses, just like arrays are -- arrays point to data memory, functions point to code memory. The program calls a function by its address. Try this: printf("%p\n", printf); I think C even lets you take the address of main, but C++ doesn't.

You definitely do have to use * for pointers to structures! Either that or the -> operator, which is just a short-form thing which does two things at once. These two statements are equivalent: (*structptr).member vs structptr->member

The same applies to classes, which are just a refinement of structures.

1 Like

While I was reading more about pointer in C, I came up another silly question for the same "call-by-reference swap example" here.

#include <stdio.h>

void swap1 (int *x, int *y);

int main ()
{
  int a, b;
  a = 10; 
  b = 20;
  printf ("Original a = %d    b = %d\n", a, b);
  swap1 (&a, &b);
  printf ("After calling swap1, a = %d b = %d\n", a, b);
  return 0;
}

void swap1 (int *x, int *y)         //Compiled without error but did not work!
{
  int t;
  t = *x;
  x = y;           // Line 23 Correct way is *x = *y; 
  y = &t;           //Line 24 Correct way is *y = t; I was thinking the address should work too.
}
$> ./a.out
Original a = 10    b = 20
After calling swap1, a = 10 b = 20

I thought Line 23 is to assign the "address of x" with "the address of y" and then Line 24, makes y pointing to t, so that the addresses exchanged. Why my understanding is wrong?
Or, what is the difference between "x = y" and "*x = *y" from memory perspective? Thanks a lot!

No! The way to read the statements in:

void swap1 (int *x, int *y)
{
  int t;
  t = *x;
  *x = *y;
  *y = t;
}

is:
Set the contents of t to be the integer pointed to by the contents of x.
Set the contents of the integer pointed to by the contents of x to be the integer stored in the location pointed to by the contents of y.
Set the contents of the integer pointed to by the contents of y to be the integer stored in t.

1 Like

Thanks Don!
Good I thought the same way on your swap1() function as you interpreted!
Then how is my way read?

void swap1 (int *x, int *y) 
{  
 int t;   
t = *x;   
x = y;       //Line 23
y = &t;      //Line 24
}

The reason I posted here is there was no error or warning, and it did not work the way I supposed. I must have missed important thing.

Imagine that x and y are just integers. What, exactly, will these two statements cause to happen outside the function?

x = y;
y = (anything at all);

If you said 'absolutely nothing', you are correct... The integers are passed by value, changing their value does not change anything outside the function.

Now switch them out for pointers again... What changes? Once again, absolutely nothing. You changed the values of X and Y, not their contents. Pointers are just fancy integers. Assigning something to a pointer does not magically change its contents.

// Assigning a value to a pointer
x = somethingelse;
// Assigning a value to the contents of a pointer
(*x) = somethingelse;

Thanks, I think this is the part I am not clear about:
You changed the values of X and Y, not their contents.
Does that imply if I want to change the contents of pointers X and Y, only expression *X = *Y must be used? Please confirm this, thank you!

#include <stdio.h>

void swap1 (int *x, int *y);

int main ()
{
  int a, b;
  a = 10; 
  b = 20;
  printf ("Original a = %d    b = %d\n", a, b);
  swap1 (&a, &b);
  printf ("After calling swap1, a = %d b = %d\n", a, b);
  return 0;
}

void swap1 (int *x, int *y)         //Compiled without error but did not work!
{
printf("Before swap, the values of x= %p, y = %p\n", x, y);
printf("Before swap, the contents of x= %d, y = %d\n\n", *x, *y);

  int t;
  t = *x;
  x = y;           // Line 23 Correct way is *x = *y; 
  y = &t;           //Line 24 Correct way is *y = t; But it did change the value of y, cf. the ./a.out part:  y = 0x7fffc542206c.

printf("After swap, the values of x= %p, y = %p\n", x, y);
printf("After swap, the contents of x= %d, y = %d\n\n", *x, *y);

}

When I checked the swap function, it seems x and y got swapped, but not a and b.

yifangt@mint $ ./a.out
Original a = 10    b = 20
Before swap, the values of x= 0x7fffc5422088, y = 0x7fffc542208c
Before swap, the contents of x= 10, y = 20

After swap, the values of x= 0x7fffc542208c, y = 0x7fffc542206c             //because y = &t Line 24
After swap, the contents of x= 20, y = 10

After calling swap1, a = 10 b = 20
yifangt@mint $ 

I have a strong feeling but very vague idea of the stack/heap for this swap-function calling in the memory, especially when the code did not work without an error nor any warning. Thanks a lot!

It is simple: If you want to change the pointer, use x = ... ; if you want to change the object the pointer points to, use *x = ... .

Thanks a lot!

Yes! This is exactly what I mean.

Pointers never, ever, ever access their contents without being explicitly told, with the * -> operators. This is why I keep comparing them to dumb integers -- they are. No integer or assignment operator is going to assume you want a pointer's contents instead of its value, unless you dereference it with the * -> operators.

1 Like