This program contains a typical node struct for a linked list (so a pointer to the next node and an int containing a value). I have the following test functions:
void F(NODE** Y, int value)
{
NODE* X = *Y;
if(!X)
{
printf("case1...\n");
X = (NODE*)malloc(sizeof(NODE));
X->Data = value;
return;
}
printf("case2...\n");
X->Next = (NODE*)malloc(sizeof(NODE));
X->Next->Data = value;
return;
}
void myPrint(NODE** Y)
{
NODE* X = *Y;
printf("printing...\n");
printf("%d %d\n", X->Data, X->Next->Data);
return;
}
int main()
{
NODE* n = NULL;
F(&n, 5);
F(&n, 10);
myPrint(&n);
}
This code produces the following output in Linux:
case1...
case1...
printing...
Segmentation fault
I don't understand why passing a null pointer to the function will always cause the first case to occur. It seems like the pointer is being passed by value, but I don't think that's what's going on. If I call malloc() on the node inside of main() and then pass it to F(), the second case will be hit but never the first. This at least makes partial sense to me, since the node will never be null when it is passed to F() from main(), but obviously allocating the node before passing it to F() means that the null check inside F() will never be true.
Is what I'm trying to do even possible? Is there a way to pass n to F() while it's null and have it behave the way I want it to? Or do I have to allocate n outside of F() and remove the null check inside of F()?
After allocation for X you need to set it in *Y in the function F(). Otherwise, it does not get reflected once F() returns.
So I would change code to
void F(NODE** Y, int value)
{
NODE* X = *Y;
if(!X)
{
printf("case1...\n");
X = (NODE*)malloc(sizeof(NODE));
X->Data = value;
*Y = X; //set the allocated pointer
return;
}
printf("case2...\n");
X->Next = (NODE*)malloc(sizeof(NODE));
X->Next->Data = value;
return;
}
In the function F() when the 'n' in main() contains NULL;
the code is not setting the contents of 'n' to point to the malloc'd memory.
Changing the value contained in the local variable 'X' has no effect on the 'n' in main()
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.
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.
For a month or two since i started learning data-structures , using C, i have been following a particular method of writing linked list. Which looks like this.
#include<stdio.h>
#include<stdlib.h>
struct Node{
int exponent;
int coeff;
struct Node *next;
};
typedef struct Node N;
N *st = NULL;
void insert(N *node, int c, int e){
N *temp;
node->exponent = e;
node->coeff = c;
if(st == NULL){
node->next = st;
st = node;
} else {
temp = st;
while(temp->next != NULL){
temp = temp->next;
}
node->next = temp->next;
temp->next = node;
}
printf(" %p", st); //this is written on purpose, not that i write it everytime
}
and i call this from the main method,
N *node = malloc(sizeof *node);
insert(node, 1, 2);
The output of the printf for four such calls is
00340D18 00340D18 00340D18 00340D18
i.e the value of the start pointer remains constant, but if i make a small change in the code
typedef struct Node N;
void insert(N *node, N *st, int c, int e){
N *temp;
node->exponent = e;
node->coeff = c;
if(st == NULL){
node->next = st;
st = node;
} else {
temp = st;
while(temp->next != NULL){
temp = temp->next;
}
node->next = temp->next;
temp->next = node;
}
printf(" %p", st);
}
and declare the the start pointer in the main method
N *strt = NULL;
node = malloc(sizeof *node);
insert(node, strt, 1, 1);
then run this four times like in the previous case, the values of start pointer gets changed
after each call
00560D18 00560D30 00560D48 00560D60
Why does this happen?
And if i want to pass the start pointer as a parameter what changes should be made?
Why does this happen?
This happens because the change to st is invisible to the caller. That is st = node has no effect whatsoever for the caller. The function changes its own copy and after the function returns, if the caller prints strt, it will still be NULL.
This is a somewhat subtle consequence of the fact that in C arguments are passed by value, even pointers. So you pass strt by value. You can change st->whatever because it's a pointer and changes will propagate to the caller but changing strt itself will not work.
And if i want to pass the start pointer as a parameter what changes
should be made
This is a regular question on this site and there is also a C FAQ that describes the problem. A simple if somewhat cumbersome change that you can do is have the function take a
N **st and pass &strt.
This is because strt in your main method and st in the modified function insert are two different variables. The function call
insert(node, strt, 1, 1);
copies the value of strt which is defined in main to the function parameter st which is a different variable and is allocated on the stack when the function insert is invoked. Any changes made to st is visible inside the function only because it's a local variable. It goes out of scope once the function returns. Therefore, strt defined in main is still pointing to null and never gets changed. This means that the condition st == NULL is always true and the if block in insert is always executed and the local variable st is set to the newly created node each time the function insert is called. This would, in fact, cause memory leak because you lose the handle on the node once the function insert returns.
What you should do is to pass an address of the variable strt to insert so that the changes made to it is visible in main. Since you always append the new node at the end of the linked list, I suggest a few more changes.
void insert(N *node, N **st, int c, int e) {
N *temp = *st;
node->exponent = e;
node->coeff = c;
node->next = NULL; // set it explicitly to NULL
if(*st == NULL) { // if head of the linked list is NULL
*st = node;
}
else {
while(temp->next != NULL) // reach the end of the linked list
temp = temp->next;
temp->next = node; // add the new node at the end
}
printf("%p", *st);
}
And in main, invoke the function as
// in main method
N *strt = NULL;
node = malloc(sizeof *node);
insert(node, &strt, 1, 1);
I have a problem with my pointers and structures in C (I know, I knooww, pretty basic!). I was practicing my procedural paradigm. It's the first time I use a debugger, because I haven't really needed it earlier in my life : < so I if you please help me I'll be thankful.
I defined the following structure to make a list:
typedef struct node {
int info;
struct node *next;
struct node *prev;
} node_t;
And then this function to fill it up:
void addNodo(node_t * list, int x){
node_t * pointer;
node_t * temp;
temp = (node_t *)malloc(sizeof(node_t));
temp->info = x;
temp->next = NULL;
temp->prev = NULL;
pointer = list;
if(pointer == NULL){ //it's empty so start it
list = temp;
return;
}
if (pointer->info <= x) { //I like my lists tidy so...
while((pointer->next != NULL) && (pointer->info <= x)){
pointer = pointer->next;
}
if(pointer->next == NULL){
pointer->next = temp;
temp->prev = pointer;
return;
}
pointer->next->prev = temp;
temp->next = pointer->next;
temp->prev = pointer;
pointer->next = temp;
return;
}
}
And then, doing this:
int main(int argc, char** argv) {
node_t * list = NULL;
addNodo(list, 1);
printf("x: %d", list->info);
return (EXIT_SUCCESS);
}
It's throwing me a Segmentation Error! When I debug it everything is fun and games until it passes the ++++ line, list address goes back to 0x0 and can't get it to work. I know there's an error somewhere, but to my knowledge of pointers, it's perfectly fine. Please, detect my error and teach me some pointers.
When you call addNode() you're passing in the pointer by value. So when you change it in the body of the function the change is lost and doesn't propagate outside the function. You need to declare it as:
void addNode(node_t **pointer, int x)
and then use *pointer in the function.
And when you call ity in main, pass in &list
The problem is that you cannot modify list inside the addNodo function. In C parameters are sent by value, so the changes you are doing inside "addNodo" is local to there.
You need to change addNodo function so, it actually receives the direction where is list.
void addNode(node_t **list, int x){
...
if(*pointer==NULL){
*list = temp;
}
}
Then in your main you should use:
addNode(&list, 1);
Well, you are making the mistake of passing the address of the list by value. So all the arguments of the function are made copies of and then your addNodo() works on the copied variables. Thus the original list does not get modified.
What you should be doing while calling is this:
addNodo(&list, 1);
In the function make these changes:
void addNodo(node_t ** list, int x)
/* This will enable you to get a copy of the address of the list variable.
Please note that this is also pass by value, C does not support pass by
reference */
Then make this change:
pointer = *list;
/* this will make the pointer point to the beginning of list as now
list is a pointer to pointer type */
Hope it helps you.
BTW, please go through a standard C book (I recommend K&R) to get familiar with passing arguments in C and what happens internally.
You're making a classic mistake:
void addNodo(node_t * list, int x)
...
list = temp;
return;
list isn't changed in the caller (main())
You can change the values in the memory list points at, but you can't change the value of list and have it be seen by the caller.
In order to do that, you'd need to pass a pointer to a pointer into the function:
void addNodo(node_t **list int x)
This allows you to change what list points at by doing:
*list = temp;
typedef struct node
{
struct node *leftChild, *rightChild;
int value;
} bst;
void insert(bst* b, int i)
{
b=malloc(sizeof(b));
b->value=i;
b->leftChild = NULL;
b->rightChild = NULL;
printf("[%i]",b->value);
return;
}
main()
{
bst* b;
insert(b,5);
printf("[%i]",b->value);
}
The second printf causes a Segmentation Fault, commenting it out it works fine. What am I doing wrong here (Ignore the meanings of the function names i.e. that the insert is an insert function for the bst)? Shouldn't bst persist in it's setup outside of the insert function?
You need to pass the pointer by reference to change where it points.
// just add this & here in the function definition.
// That's the only change in all your code (for c++)
void insert(bst * &b, int i)
Or in C, a pointer pointer
void insert(bst ** b, int i)
{
*b=malloc(sizeof(*b));
(*b)->value=i;
(*b)->leftChild = NULL;
(*b)->rightChild = NULL;
printf("[%i]",(*b)->value);
return;
}
Note the changes to dereference the pointer pointer.
When you call this function, you'll need to take the address of your bst *:
insert(&b,5);
The problem is that b in main points to an invalid memory. The b doesn't get changed by insert because insert uses a copy of the pointer. You have to options to solve this:
Make "insert" take a pointer to pointer
Allocate the memory before entering "insert"
Because the first one is harder to read (and understand), I'd prefer the second one. But it depends on you what variant you choose.
Memory allocation before insert:
typedef struct node
{
struct node *leftChild, *rightChild;
int value;
} bst;
void insert(bst* b, int i)
{
// No malloc here
b->value=i;
b->leftChild = NULL;
b->rightChild = NULL;
printf("[%i]",b->value);
return;
}
main()
{
bst* b = (bst *)malloc(sizeof(bst)); // This line has changed
insert(b,5);
printf("[%i]",b->value);
}
The variant with a double pointer:
typedef struct node
{
struct node *leftChild, *rightChild;
int value;
} bst;
void insert(bst** b, int i)
{
*b = (bst *)malloc(sizeof(bst)); // This line has changed
(*b)->value=i;
(*b)->leftChild = NULL;
(*b)->rightChild = NULL;
printf("[%i]", (*b)->value);
return;
}
main()
{
bst* b;
insert(&b,5); // Now b is changed
printf("[%i]",b->value);
}
Side note 1: you also have the malloc() incorrect - you should put use sizeof(bst) instead of sizeof(b) as the size. This is because sizeof(b) == sizeof(bst *) == size of an address, which is almost certainly not what you want.
Side note 2: do not forget to call free(b) at the end to avoid memory leaks.
Side note 3: check that malloc() doesn't return NULL. A rare case but a good program should expect that.
Since C does pass-by-value for arguments, the pointer being referenced inside insert() is a copy of the original b. The memory gets allocated to the variable b inside the function, but not to the variable in the main()function.
for eg.
# In main
b<main> = 0xOrigLoc
b<insert> = undefined
# In insert (before malloc)
b<main> = 0xOrigLoc
b<insert> = 0xOrigLoc
# After Malloc
b<main> = 0xOrigLoc
b<insert> = 0xNewLoc
Notice how b<main> remains untouched. You are only tweaking the copy of b inside the function.
The solution is to use a pointer to a pointer as described by the previous solution.
Rather than change the value, return the new value of b
bst* insert(bst* b, int i)
{
if (b == NULL)
{
bst* result = (bst*)malloc(sizeof(bst));
result->value=i;
result->leftChild = NULL;
result->rightChild = NULL;
printf("[%i]",b->value);
return result;
}
if (i < b->value)
b->left = insert(b->left, i);
else
b->right = insert(b->right, i);
return b;
}
int main()
{
bst* b = NULL; // Initialise it to empty.
b = insert(b,5);
printf("[%i]",b->value);
}
Well one problem is you're not typecasting your pointer.
The line:
b=malloc(sizeof(b));
Should be:
b=(bst*)malloc(sizeof(b));
Your code gives me an error when I compile it in gcc.
This is how to identify and fix errors in the future:
Testing in GDB:
Full code:
#include <malloc.h>
#include <stdio.h>
typedef struct node
{
struct node *leftChild, *rightChild;
int value;
} bst;
void insert(bst* b, int i)
{
b=(bst*)malloc(sizeof(b));
b->value=i;
b->leftChild = NULL;
b->rightChild = NULL;
printf("[%i]",b->value);
return;
}
main()
{
bst* b;
insert(b,5);
printf("[%i]",b->value);
}
Compiled with
%g++ -g main.cc
Ran with
%gdb a.out
GDB commands:
b main.cc:211
run
print b
(output is: 0x0)
step
step
print b
(output is: 0x4f60010)
step
step
step
step
step
print b
(output is: 0x0)
quit
This clearly indicates that your variable is getting deallocated when the function leaves its scope.
Changing the code to:
#include <malloc.h>
#include <stdio.h>
typedef struct node
{
struct node *leftChild, *rightChild;
int value;
} bst;
void insert(bst* b, int i)
{
b->value=i;
b->leftChild = NULL;
b->rightChild = NULL;
printf("[%i]",b->value);
return;
}
main()
{
bst* b;
b=(bst*)malloc(sizeof(bst));
insert(b,5);
printf("[%i]",b->value);
free(b);
}
and rerunning gdb with the following commands:
b main.cc:21
run
s
(short for step)
print b
(output: 0x1aab010)
s
s
s
s
s
s
print b
(output: 0x1aab010)
Your problem is clearly the scope of the allocation. Moving the allocation to main fixes this.
Yay your program now works!
This should give you insight into how to use the debugger to solve these kinds of simple errors in the future.
Edit:
As one op pointed out, your underlying problem is that pointers passed to functions in c are treated as local objects, so when you exit the function, any allocated memory to single pointers is tossed in the bitbucket.
Double pointers allow you to actually allocate within functions, you just have to be careful to dereference them as necessary. So your working code could also be:
#include <malloc.h>
#include <stdio.h>
typedef struct node
{
struct node *leftChild, *rightChild;
int value;
} bst;
void insert(bst** b, int i)
{
(*b)=(bst*)malloc(sizeof(bst));
(*b)->value=i;
(*b)->leftChild = NULL;
(*b)->rightChild = NULL;
printf("[%i]",(*b)->value);
return;
}
main()
{
bst* b;
insert(&b,5);
printf("[%i]",b->value);
free(b);
}
bst* b; declares a pointer... but doesn't allocate the actual structure.
allocate and initialize b and you'll be onto your next step.
This is slightly a style discussion but your main should look either like this.
main()
{
bst *b;
b = (bst*)malloc(sizeof(bst));
/* more initialization */
insert(b, 5);
free(b);
}
or like this
main()
{
bst *b;
bst_new(&b);
insert(b, 5);
bst_free(&b);
}