This function does not return the intended result (swapping a
and b
).
#include<stdio.h>
#include<stdlib.h>
void swap_one(int *x, int *y) {
int *tmp;
tmp = x;
x = y;
y = tmp;
printf("x = %d y = %d\n", *x, *y);
}
void swap_two(int *x, int *y) {
}
int main() {
int a = 5;
int b = 100;
printf("Before a = %d b = %d\n\n", a, b);
int *p = (int *) malloc(sizeof(int));
int *q = (int *) malloc(sizeof(int));
p = &a;
q = &b;
swap_one(p, q);
printf("After a = %d b = %d\n", a, b);
return 0;
}
But the below code works as expected.
#include <stdlib.h>
#include <stdio.h>
typedef struct ListElmt_ {
void *data;
struct ListElmt_ *next;
} ListElmt;
typedef struct List_ {
int size;
int (*match) (const void *key1, const void *key2);
void (*destroy) (void *data);
ListElmt *head;
ListElmt *tail;
} List;
void list_init (List *list) {
list->size = 0;
list->match = NULL;
list->destroy = NULL;
list->head = NULL;
list->tail = NULL;
}
int list_ins_next(List *list, ListElmt *element, void *data) {
ListElmt *new_element;
/* Alocate storage for the element. */
if ((new_element = (ListElmt *) malloc(sizeof(ListElmt))) == NULL) return -1;
/* new_element->data is of type void *. So we use (void *) data */
new_element->data = (void *)data;
if (element == NULL) {
/* Handle insertion at the head of the list */
if (list->size == 0) list->tail = new_element;
new_element->next = list->head;
list->head = new_element;
} else {
if (element->next == NULL) list->tail = new_element;
new_element->next = element->next;
element->next = new_element;
}
list->size ;
return 0;
}
/* Print the list */
static void print_list(const List *list) {
ListElmt *element;
int *data;
int i;
/* Display the linked list */
fprintf(stdout, "List size is %d\n", list->size);
i = 0;
element = list->head;
while (1) {
data = element->data;
fprintf(stdout, "list[d] = d\n", i, *data);
i ;
if (element->next == NULL) {
break;
} else {
element = element->next;
}
}
}
int main(int argc, char **argv) {
List list;
ListElmt *element;
int *data;
int i;
/* list = (List *) malloc(sizeof(List)); */
/* Initialize the linked list */
List *listPtr;
listPtr = &list;
list_init(listPtr);
/* Perform some linked list operations */
element = listPtr->head;
for (i = 10; i > 0; i--) {
if ( (data = (int *) malloc(sizeof(int))) == NULL) return 1;
*data = i;
if (list_ins_next(listPtr, NULL, data) != 0) return 1;
}
print_list(listPtr);
fprintf(stdout, "Value in *data is:%d\n", *data);
return 0;
}
Question is: In the swap_one
function, x=y
is similar to new_element->next = element->next
or element->next = new_element
. Why do new_element->next = element->next
and element->next = new_element
work but x =y
in the swap_one function does not swap a
and b
?
Sorry for lot of the code but I am genuinely confused about this one.
Thanks.
CodePudding user response:
The pointers are passed by value. Swapping the pointer values held in those variables does not achieve a swap of the data they point to.
Imagine that
&p
is the address 0x1000 and&q
is the address 0x1004.Now, you call
swap_one(&p, &q);
-- the value of the pointerx
is 0x1000 and the value of the pointery
is 0x1004.You now swap the pointer values.
x
is now 0x1004 which points atq
andy
is now 0x1000 which points atp
. However,p
andq
never moved in memory. You only changed the values stored in your pointers.When the function returns, those pointers go out of scope.
p
andq
still hold the same contents as before, because you never modified them in the first place.
So, to swap the values they point at, you must dereference the pointer to get at the actual data.
int tmp = *x;
*x = *y;
*y = tmp;
Contrast this to your linked list example. In that example, you had a pointer to a structure, and you were modifying its next
member. This actually modifies the pointer in memory, because that's where your linked list resides and how it stores values.
However, the same issue would occur if you gave these pointers to some hypothetical function: swap_next(node **x, node **y);
-- x
and y
are just local variables pointing at a pointer value. If you swap them, they do not modify anything other than themselves. So the same fix applies: to change data being pointed at, you must follow the pointer.
CodePudding user response:
Your swap_one
function is only swapping the pointer values passed to the function, not what the pointers are pointing to. So any change you make won't be reflected outside of the function.
You need to dereference those pointers to read/write what they point to.
void swap_one(int *x, int *y) {
int tmp;
tmp = *x;
*x = *y;
*y = tmp;
printf("x = %d y = %d\n", *x, *y);
}
In the list_ins_next
function, you are changing what the pointers point to, so the changes are seen outside of the function.
CodePudding user response:
The variable x
and y
are local variable of swap_one()
.
This is what happening when you calling swap_one()
function:
p = &a;
q = &b;
p (pointer) q (pointer)
--- ---
| |--- | |---
--- | --- |
| |
a ---- b ----
| | | |
---- ----
Calling swap_one(p, q)
function from main()
function:
p (pointer) q (pointer)
--- ---
| |--- | |---
--- | --- |
| |
a ---- b ----
| | | |
---- ----
| |
| |
--- | --- |
| |--- | |---
--- ---
x (pointer) y (pointer)
pointing to a pointing to b
(x and y are parameters of swap_one())
After execution of these statements of swap_one()
function:
tmp = x;
x = y;
y = tmp;
the pointer x
will point to address that pointer y
was pointing at and pointer y
will point to address that pointer x
was pointing at when swap_one()
was called.
p (pointer) q (pointer)
--- ---
| |--- | |---
--- | --- |
| |
a ---- b ----
| | ----- | |
---- | ----
| |
--------|---------
--- | | ---
| |--- ---| |
--- ---
x (pointer) y (pointer)
pointing to b pointing to a
Note that pointer p
and pointer q
are still pointing to variable a
and b
respectively. That's why when swap_one()
function returned, the value of variable a
and b
is not swapped.
If you want to swap the value of the variables, whose address passed as arguments to swap_one()
function than dereference the pointer parameters and replace the value at the location, i.e. you should do following in swap_one()
function:
tmp = *x;
*x = *y;
*y = tmp;
With these changes the values of variable whose address passed to swap_one()
function will be swapped because, now, you are dereferencing the pointer x
(i.e. *x
) and pointer y
(i.e. *y
) and assigning the values at that location.
Now, coming to this part of second code. Taking the case when element
is not NULL
} else {
if (element->next == NULL) list->tail = new_element;
new_element->next = element->next;
element->next = new_element;
Note that element
pointer is of type ListElmt
and yes it is also a local pointer variable of list_ins_next()
function. But, here we are making use of this local pointer variable to modify the value of structure member which it is pointing to.
new_element (pointer of type ListElmt)
----
| |-----
---- |
|
-----------
| | |
-----------
^ ^
| |
data next
pointer pointer
element (pointer of type ListElmt, which is pointing to an existing node of list
---- passed to list_ins_next() function)
| |-----
---- |
|
-----------
| | |
-----------
^ ^
| |
data next
pointer pointer
Note that this
new_element->next
is same as
(*new_element).next
which means, dereferencing the new_element
pointer (go to the location where it is pointing to) and access the next
pointer at that location.
So, this
new_element->next = element->next;
will make the new_element->next
pointer pointing to what element->next
pointer is pointing and this
element->next = new_element; // same as (*element).next = new_element;
will make the element->next
pointer pointing to what new_element
pointer is pointing at. After execution of these statements,
new_element (pointer of type ListElmt)
----
| |-----
---- |
|
-----------
--- | | |-------> (pointing to what element->next was pointing at)
| -----------
| ^ ^
| | |
| data next
| pointer pointer
--------------------
|
element |
| |----- |
---- | |
| |
----------- |
| | |----
-----------
^ ^
| |
data next
pointer pointer
So, if you want to make changes in the pointer that you are passing to a function, you have to dereference it in the calling function.
If you are still confused then only check this:
In your second code if you do same as what you are doing in first code:
example = <some pointer of type ListElmt>;
i.e. assign some other pointer of same type to example
, this is what will happen:
element
---- -----------
| |---------------> | | |
---- -----------
(The original ListElmt type element
whose address passed to list_ins_next() function)
-----------
| | |
-----------
^ ^
| |
data next
pointer pointer
Now, when list_ins_next()
function will returned, the original element whose address was passed to this function will remain unchanged.
Additional:
Here, your program is leaking memory:
int *p = (int *) malloc(sizeof(int));
int *q = (int *) malloc(sizeof(int));
p = &a;
q = &b;
because the allocated memory references, to pointer p
and q
, (using malloc()
) will be lost when you are assigning &a
and &b
to p
and q
respectively.