Concerning double indirection (pointer to pointer) and passing those to a function
I cannot change the pointer here in function void test(int **nmbr, int *n);
int n = 5;
int n2 = 555;
int *nPtr = &n;
int *n2Ptr = &n2;
printf("the number = %d\n", *nPtr);
test(&nPtr, n2Ptr);
printf("the number is now = %d\n", *nPtr);
test
void test(int **nPptr, int *n2Ptr) {
int *p = *nPptr;
p = n2Ptr;
}
since the pointer p points to a copy of *nPtr, right?
But what about this code (this time the pointer points to a given struct in a linkedlist
the code snipped is from the site https://www.learn-c.org/en/Linked_lists
int remove_by_index(Person **head, int n) {
int i = 0;
int retval = -1;
Person *current = *head;
Person *temp_node = NULL;
if (n == 0) {
return pop_first(head);
}
for (i = 0; i < n-1; i++) {
if (current->next == NULL) {
return -1;
}
current = current->next;
}
temp_node = current->next;
retval = temp_node->nmbr;
current->next = temp_node->next;
free(temp_node);
return retval;
}
it removes a given node in the list by a given indexnumber
Here one can see that *current is local copy in the function and traversate in the list and lastly merges two nodes, without problem
So why does it work to change the pointer here but not in the function test(int **nPptr, int *n2Ptr)?
To be clear
in the function test:
int *p = *nPptr;
p is local copy and copies the pointer from *nPtr
in the function remove_by_index
Person *current = *head;
current is local copy and copies the pointer from *head. The list lives beyond the scope of the function remove_by_index(..) so I do not understand why it can be manipulated in the function by the local pointer *current, at the same time as it does not work to alter nPtr in function test(..)
In a function, changes to pointer variables or pointer parameters have no effect outside the function. However, if the pointer is pointing to an object outside the function, that object can be modified by dereferencing the pointer.
For example, in OP's test function:
void test(int **nPptr, int *n2Ptr) {
int *p = *nPptr;
p = n2Ptr;
}
p is initialized and then its value is changed by an assignment. This has no effect on any object outside the function. If the function were changed as follows:
void test(int **nPptr, int *n2Ptr) {
int *p = *nPptr;
p = n2Ptr;
*nPptr = p;
*p = 42;
}
Then two objects outside the function will have been modified (an int * and an int).
In OP's remove_by_index function, changes to the current variable as it progresses through the linked list have no external effect, but the line:
current->next = temp_node->next;
is equivalent to:
(*current).next = (*temp_node).next;
The external Person object that current is pointing to on the linked list has been modified by dereferencing of the pointer and assignment to the next member of the Person it is pointing to.
Related
I want this function to add a point to the beginning of the linked list:
void addPoint(Point *head, int x, int y, SDL_bool dir) {
Point *p = malloc(sizeof *p);
p->x = x;
p->y = y;
p->dir = dir;
p->next = head;
head = p;
}
The head is initialized earlier like so:
Point *down = NULL;
Afterwards I call the function like so:
addPoint(&down, x * grid_cell_width, (y - 1) * grid_cell_height, SDL_FALSE);
Unfortunately this does not work as After after the call the head is still NULL.
You are passing a pointer to head, meaning you are passing a copy of the address of head to function addPoint. In the line where you are changing the value of head you are actually modifying the local pointer. To change that, you need to pass a pointer to a pointer, like so:
void addPoint(Point **head, int x, int y, SDL_bool dir) {
Point *p = malloc(sizeof(Point));
p->x = x;
p->y = y;
p->dir = dir;
p->next = head;
*head = p;
}
You got this part right:
addPoint(&down,...);
but the type of the expression &down is Point **, not Point *, so you need to change the definition of addPoint to
void addPoint(Point **head, int x, int y, SDL_bool dir) {
...
p->next = *head;
*head = p;
}
With this code, you have the following relationships:
head == &down // Point ** == Point **
*head == down // Point * == Point *
So in the addPoint function, writing to *head is the same as writing to down.
Try something like:
void addPoint(Point **head, int x, int y, SDL_bool dir) {
Point* p = malloc(sizeof(p));
p->x = x;
p->y = y;
p->dir = dir;
p->next = head;
*head = p;
}
In C there is no pass-by-reference mechanism per se, you just pass pointers to arguments instead (in this case, a pointer to a pointer). But those pointers are still just values, so a change to them is not visible outside of the function. What will be visible is a change to the value that the pointer points to. That's why you need to assign your p pointer to the location that the pointer head points to, hence you need to dereference it with (*) operator first.
First of all I'd like to say that I'm quite new to pointers and linked lists so this is quite obscure to me still.
My goal in this program is to use strcmp as a tool to compare my data and therefore swap datas if necessary.
Here's my code :
#include "ft_list.h"
#include <stdlib.h>
#include <stdio.h>
void ft_print_list_data(t_list *list)
{
while (list)
{
printf("%p\n", list->data);
list = list->next;
}
}
t_list *ft_create_elem(void *data)
{
t_list *list;
list = NULL;
list = malloc(sizeof(t_list));
if (list)
{
list->data = data;
list->next = NULL;
}
return (list);
}
void ft_list_sort(t_list **begin_list, int (*cmp)())
{
t_list *list_ptr;
t_list *next_node;
t_list *temp;
list_ptr = *begin_list;
if (list_ptr)
{
while (list_ptr->next)
{
next_node = list_ptr->next;
while (next_node->next)
{
if ((*cmp)(list_ptr->data, next_node->data) > 0)
{
printf("if\n");
temp = next_node->next->data;
list_ptr->data = next_node->data;
next_node->data = temp;
}
next_node = next_node->next;
}
list_ptr = list_ptr->next;
}
}
}
int ft_strcmp(char *s1, const char *s2)
{
int x;
x = 0;
while (s1[x] == s2[x] && s1[x] != '\0' && s2[x] != '\0')
x++;
return (s1[x] - s2[x]);
}
int main(void)
{
char str1[] = "a";
char str2[] = "d";
char str3[] = "c";
char str4[] = "b";
char str5[] = "g";
char str6[] = "f";
char str7[] = "e";
t_list *begin_list;
begin_list = ft_create_elem(&str1);
begin_list->next = ft_create_elem(&str2);
begin_list->next->next = ft_create_elem(&str3);
begin_list->next->next->next = ft_create_elem(&str4);
begin_list->next->next->next->next = ft_create_elem(&str5);
begin_list->next->next->next->next->next = ft_create_elem(&str6);
begin_list->next->next->next->next->next->next = ft_create_elem(&str7);
printf("list\n");
ft_print_list_data(begin_list);
ft_list_sort(&begin_list, &ft_strcmp);
printf("updated\n");
ft_print_list_data(begin_list);
return (0);
}
Here's what I'm getting in the terminal :
list
0x7ffcfc6615ea
0x7ffcfc6615ec
0x7ffcfc6615ee
0x7ffcfc6615f0
0x7ffcfc6615f2
0x7ffcfc6615f4
0x7ffcfc6615f6
if
if
if
if
updated
0x7ffcfc6615ea
0x7ffcfc6615f0
0x7ffcfc6615f0
0x7ffcfc6615f4
0x7ffcfc6615f6
0x7ffcfc6615f6
0x7ffcfc6615f6
As you can see I get duplicates instead of swaps. I have no clue why, I guess I'm not understanding well how pointers work so I'm, as it seems, overwritting in some way.
I would really appreciate if a gentle soul could show me the light on what I'm misunderstanding !
Thank you for you help.
In ft_list_sort, the nodes whose data member values are being compared are not the same as the nodes whose data member values are being swapped:
if ((*cmp)(list_ptr->data, next_node->data) > 0)
{
printf("if\n");
temp = next_node->next->data;
list_ptr->data = next_node->data;
next_node->data = temp;
}
Specifically, the first line of the swap code temp = next_node->next->data; is completely unrelated to the node data member values being compared. Changing that line to temp = list_ptr->data; fixes that problem:
if ((*cmp)(list_ptr->data, next_node->data) > 0)
{
printf("if\n");
temp = list_ptr->data;
list_ptr->data = next_node->data;
next_node->data = temp;
}
Another problem is that while (next_node->next) does not consider the final node of the list for swapping. Changing it to while (next_node) fixes that problem.
Another problem is that the temp variable has the wrong type. It should be the same type as the data member of t_list, but if that is a pointer type, temp can be type void *.
Another problem is that the cmp function pointer has no prototype. It should probably be something like int (*cmp)(const void *, const void *), but the prototype of the function it points to would need the same prototype.
By changing if (list_ptr) to while (list_ptr) a level of indentation can be removed.
Putting all that together:
void ft_list_sort(t_list **begin_list, int (*cmp)(const void *, const void *))
{
t_list *list_ptr;
t_list *next_node;
void *temp;
list_ptr = *begin_list;
while (list_ptr)
{
next_node = list_ptr->next;
while (next_node)
{
if ((*cmp)(list_ptr->data, next_node->data) > 0)
{
printf("if\n");
temp = list_ptr->data;
list_ptr->data = next_node->data;
next_node->data = temp;
}
next_node = next_node->next;
}
list_ptr = list_ptr->next;
}
}
For the comparison function, assuming you want to leave ft_strcmp as is, you will need a wrapper function with the correct prototype:
int my_datacmp(const void *a, const void *b)
{
return ft_strcmp(a, b);
}
(Note: in ft_strcmp, both parameters should be type const char *. The current version is missing the const for one of the parameters.)
Then pass my_datacmp in the call to ft_list_sort:
ft_list_sort(&begin_list, &my_datacmp);
(Note: You can use my_datacmp instead of &my_datacmp because both expressions are pointers to the my_datacmp function.)
this is a little tricky and I might not be able to explain it well, but Im gonna try anyway.
In this swap
temp = next_node->next->data;
list_ptr->data = next_node->data;
next_node->data = temp;
you are merely swapping the pointers, and not the actual data...
Lets say, the if statement if ((*cmp)(list_ptr->data, next_node->data) > 0) is true, then
temp = next_node->next->data; // temp is assigned pointer to the next->data
list_ptr->data = next_node->data;
next_node->data = temp;
After this, next_node->data is pointing to the next_node->next->data, but so is the list_ptr-data, because it was assigned so in previous command. If you are trying to copy the actual data, not just asign one pointer to another, then maybe using strcpy() is the right way to go.
So after all is done, you have two pointers, which are pointing to the same space (from which the duplicates probably come from), and you have lost the pointer to the list_ptr->data, because you reassigned it point to next_node->data
This is probably very confusing, so to sum it up, if you want to change the actual data and not just reassign the pointers, I suggest using
temp = list_ptr->data;
strcpy(list_ptr->data, next_node->data);
strcpy(next_node->data, temp);
Also I think you probably meant to use temp = next_node->next->data instead of temp = next_node->next->data ,as I dont see the reason you want to take the data of the next item, when you used your comparing if statement on temp = next_node->data
Normal Suggestion, I don't know what is the code for t_list , but i think temp should be of the same datatype of the 'data' member of your t_list structure.
Generally, I know that a pointer stores the memory address of another value located in computer memory, for example:
int firstvalue= 5
int * p1;
p1 = &firstvalue; // p1 = address of firstvalue
What happens if we define an operation like the following in a linked list? Does *current=*list means that the value pointed to by current equals to the value pointed to by list? And what does it mean if we define ecur=current?
int function(struct list_t **list){
struct list_t *ecur=NULL;
struct list_t *current=*list;
ecur=current;
}
Update:
What does it do *list=remove(*list, param1, param2)? And why is that?
remove is a function that returns a modified list of list.
Update 2:
Why do we need to define a pointer to pointer in order to modify the list? Is *list a pointer to pointer?
The variable list is a pointer to a pointer to a struct list_t. If we (just as an example) assume that the struct is placed at address 2000 and that the unnamed pointer is at address 1000 it will look like this:
Then you have the initialization that adds two new variables. Both as pointer to a struct list_t.
struct list_t *ecur=NULL;
struct list_t *current=*list;
So the picture now becomes:
Notice that current got the same value as the "some-pointer" in the middle because it is *list that was assigned to current.
Then you have the assignment:
ecur=current;
which means that ecur gets the same value as current and gives the picture:
Update: What does it do *list=remove(*list, param1, param2) ?
It changes the value of the "some-pointer" in the middle of the picture. This is for instance needed if the remove function removes the first element in a linked list.
Why do we need to define a pointer to pointer in order to modify the list? Is *list a pointer to pointer?
Remember that C passes all function arguments by value - the formal argument in the function definition is a different object in memory from the actual argument in the function call. For example:
void swap( int a, int b )
{
int tmp = a;
a = b;
b = tmp;
}
void foo( void )
{
int x = 1;
int y = 2;
swap( x, y );
}
a is a different object in memory than x, and b is a different object in memory than y, so swapping a and b has no effect on x and y. In order to swap the values of x and y, you must pass pointers to them:
void swap( int *a, int *b )
{
int tmp = *a;
*a = *b;
*b = tmp;
}
void foo( void )
{
int x = 1;
int y = 2;
swap( &x, &y );
}
The expression *a is the same as x, so writing to *a is the same as writing to x. Same for *b and y.
So, in order for a function to write to a parameter, you must pass a pointer to that parameter:
void foo ( T *arg )
{
*arg = new_value(); // writes a new value to the thing arg points to
}
void bar( void )
{
T var;
foo( &var ); // write a new value to var
}
This is true for any non-array type T. Let's replace T with a pointer type P *:
void foo( P **arg )
{
*arg = new_value(); // write a new *pointer* value to the thing arg points to
}
void bar( void )
{
P *var;
foo( &var ); // write a new pointer value to var
}
The semantics are exactly the same - all that's changed is the type.
If a function has the potential to modify a list * object (say pointing it at a new list head), then you must pass a pointer to that list * object:
void add_node( struct list_t **list, struct list_t *node )
{
if ( !*list || (node->value < (*list)->value) ) // make node new head of list
*list = node;
else
// add node somewhere else in the list
}
int main( void )
{
struct list_t *list = NULL;
...
struct list_t *node = newNode( value );
add_node( &list, node );
...
}
TYPE *p = ptype /*variable of type: TYPE * */;
is not an assignment. It's an initialization, which for an auto-matic (=on-the-stack) p can be rewritten as:
TYPE *p;
p = ptype;
(not TYPE *p; *p=ptype; /*would be a type error*/)
In terms of your example:
struct list_t *current=*list;
sets where current will point to (the same place as what *list points to (*list is also a pointer because list is a doubly-indirect pointer)) without doing anything whatsoever with what current will point at (*current) after the initialization.
All of this is just conceptual, though. Your function doesn't have any externally visible effects so an optimizing compiler should completely delete its body.
I had a similar knot in my head with this post. I'd like to rearrange your function a bit, so it's easier to understand what's going on:
int function(struct list_t **list)
{
struct list_t *current = *list;
struct list_t *ecur = current;
}
If we call this function with an element foo we essentially get this:
struct list_t foo = { .data = "foo" };
struct list_t *bar = &foo;
struct list_t **list = &bar;
struct list_t *current = *list;
struct list_t *ecur = current;
We have five declarations and five assignments. For better readability, I'll write everything down without declarations:
foo = { .data = "foo" };
bar = &foo;
list = &bar;
current = *list;
ecur = current;
Now, let's walk through it:
foo is a struct. It contains the above data-field.
bar is a pointer to struct. It contains the address of foo
list is a pointer to a pointer to struct. It contains the address of bar
current is a pointer to struct. It contains the contents of the contents of list, which is the address of foo
ecur is a pointer to struct. It's identical to current and contains the address bar
In the end we can simplify the whole example to this:
struct list_t foo = { .data = "foo" };
struct list_t *ecur = &foo;
What does it all mean?
list: Because list is a pointer to a pointer you are able to modify bar to point to something completely different, by de-referencing it (*list = ...)
current/ecur: that's what bar originally pointed too. By de-referencing you could change the data-field itself ((*ecur).data = "banana" or better ecur->data)
I hope I could clarify things and didn't make it worse ;)
Why do we need to define a pointer to pointer in order to modify the
list?
Let me add a complete program, albeit short, to illustrate it better. It defines a simply linked list and builds it while keeping it ordered. Yes, I know it would be easier to simply call qsort(), but I want to demonstrate how adding one level of indirection —the pointer to pointer— allows to insert elements smoothly, without testing for special cases.
// C pointer exercise: sort arguments
#include <stdio.h>
#include <strings.h>
#include <stdlib.h>
struct list
{
char *arg;
struct list *next;
};
int main(int argc, char *argv[])
{
// pointer to base, running pointer and pointer to pointer
struct list *base = NULL, *p, **pp;
for (int i = 1; i < argc; ++i)
{
struct list *new_entry = malloc(sizeof(struct list));
if (new_entry)
{
new_entry->arg = argv[i];
// find where to insert new entry
for (pp = &base; *pp; pp = &(*pp)->next)
if (strcasecmp(new_entry->arg, (*pp)->arg) < 0)
break;
// insertion in a simply linked list
new_entry->next = *pp;
*pp = new_entry;
}
}
// display and cleanup
for (p = base; p;)
{
struct list * tmp = p->next;
puts(p->arg);
free(p);
p = tmp;
}
return 0;
}
I cannot understand the meaning of a C code about linked lists that is using double pointers. Here is the code I am reading
struct list
{
int value;
struct list *next;
};
//Insert an element at the begining of the linked list
void insertBegin(struct list **L, int val)
{
//What does **L mean?
//Memory allocation for the new element temp
struct list *temp;
temp = (struct list *)malloc(sizeof(temp));
//The new element temp points towards the begining of the linked list L
temp->next = *L;
//Set the beginning of the linked list
*L = temp;
(*L)->value = val;
}
void loop(struct list *L)
{
printf("Loop\n");
//Run through all elements of the list and print them
while( L != NULL )
{
printf("%d\n", L->value);
L = L->next;
}
}
struct list* searchElement(struct list *L,int elem)
{
while(L != NULL)
{
if(L->value == elem)
{
printf("Yes\n");
return L->next;
}
L = L->next;
}
printf("No\n");
return NULL;
}
int main()
{
struct list *L = NULL;
insertBegin(&L,10); // Why do I need
return 0;
}
What does **L in the insertElement mean and what is the difference between the **L and the *L in the loop function? Why in the main when struct list *L = NULL is declared I should call the function insertBegin with the argument &L and not a simple L?
I guess *L is a pointer towards the first node of the linked list while **L may point toward any element of the list. However, I am not sure this is correct.
Thank you for your help!
It means "pointer to a pointer". In C pointers are passed by value, so if you want to be able to modify a pointer passed to a function outside of that function, you have to pass a pointer to it. Passing the pointer will only pass another value for it, which will be modified in the function but will not reflect a change to the value outside of it. Passing as a pointer to pointer essentially allows you to modify the value at the address passed rather than just to modify the local.
Consider those two functions:
void foo(int * p, int * t) {
p = t;
}
void foo2(int ** p, int * t) {
*p = t;
}
And the code:
int a = 1, b = 2;
int * p = &a;
foo(p, &b);
// in foo *p == 2, but here *p is still == 1, because p didn't change, only the copy that was passed to foo changed
foo2(&p, &b); // now *p == 2, p was changed in foo2
The type **L is read as pointer to a pointer to L. So if you have a pointer to an L and takes its address, that is what you get. The pattern of **L in a function argument (in C) is usually used to implement an "out parameter" - a parameter the code can update. To insert at the beginning, you need to update the pointer to the head of the list - that is why that function takes a pointer to the head as a parameter. When assigning to *L, the function updates the parameter.
L stores address of the first link in the list. Thus:
*L is contents of the first link in the list, and
&L is the address of the variable that stores the address of the first link in the list.
In other words, your only way to allocate memory for and initialize a list by passing the argument into a function is by providing &L as an argument. If you pass L as an argument, the function will receive the address of the first link, whereas instead it needs a place where to store the address of the first link.
If you want a function to write to a parameter and have that new value reflected in the caller, then you must pass a pointer for that parameter:
void foo( T *p ) // for any type T
{
*p = new_value(); // write a new value to the thing p points to
}
void bar( void )
{
T var;
foo( &var ); // foo writes a new value to var
}
If we substitute T with a pointer type Q *, then the code is
void foo( Q **p ) // for any type Q
{
*p = new_value(); // write a new value to what p points to
}
void bar( void )
{
Q *var;
foo( &var ); // foo writes a new value to var
}
The semantics in both cases are exactly the same; we want foo to update the value stored in var through the pointer p. The only difference is that in the second case var already has a pointer type, so p has to be a pointer to that pointer type.
In the code you posted, the insertBegin function updates the value stored in L, which is a pointer to the head of the list. Since the variable L in main has type struct list *, the type of the parameter L in insertBegin needs to be struct list **.
The double pointer in insertBegin is for when you are changing the location of L from where ever L is to the node you are inserting. When calling the function you need &L because you need to pass it by reference because you are changing L
For the following function:
/*this function removes the topmost item in the stack*/
void pop(Stack * S, NODE * returnNode) {
stackNode * temp;
if(S->root == NULL) {
ERROR("Sorry, cannot pop an empty stack!!\n");
}
else {
temp = S->root;
returnNode = temp->Item;/*x now points to the topmost node*/
S->root = temp->nextItem;/*stack points to the rest of the list*/
free(temp);/*returning memory to the system*/
}
}
I am expecting returnNode pointer to have the same value as the temp->Item, but when I am inspecting the value in GDB it doesn't. Am I missing something?
I should add that the temp value is being correctly set.
If you want to update a pointer as a parameter, you need to pass it's address. Otherwise, you are just updating the value on the call stack, which is local in scope.
void pop(Stack * S, NODE ** returnNode) {
stackNode * temp;
if(S->root == NULL) {
ERROR("Sorry, cannot pop an empty stack!!\n");
}
else {
temp = S->root;
*returnNode = temp->Item;/*x now points to the topmost node*/
S->root = temp->nextItem;/*stack points to the rest of the list*/
free(temp);/*returning memory to the system*/
}
}
Think of it this way,
in a function you can only modify the variable the pointer is pointing to, not the value, i.e. an address, of the pointer itself. If you want to modify the value of a pointer, you need to pass a pointer that points to it.
for example
if you have:
int k = 5, f = 15, *pk = &k, *pf = &f;
and you want to switch the values of pk and pf, you would need a function like this:
void change (int **m, int **n) {
int *help = *m;
*m = *n;
*n = help;
}
change(&pk, &pf);
printf("pk ist %i, pf ist %i\n", *pk, *pf);
/* pk ist 15, pf ist 5;*/
you should have *returnNode = temp->Item; instead of returnNode = temp->Item;.