Linked List head not being updated across function calls - c

I trying to implement my own linked list and have been messing around with the code learning about dynamic memory allocation and pointers and such. When I try to add something to my linked list I get a segfault, and upon using the debugger I realized that it was because initially my linked list's head pointer was not pointing to null and then my add function was not recognizing the head as being empty. But I have an initialize function that is setting the linked list's head pointer to NULL but for some reason once I exit out of the initialize function and into the add function, the head is no longer pointing to NULL.
Here's my code:
list.h
typedef struct node{
int value;
struct node *next;
} Node;
typedef struct list{
Node *head;
} List;
list.c
void initialize(List *l){
if(l != NULL){
l = malloc(sizeof(List));
l->head = NULL;
}
}
void add(List *l, int a){
//Code
}
int main(){
List l;
initialize(&l)
add(&l, 2);
}
As soon as I step into the add function and print out *l, I see that the head is not pointing to 0x0. And I've been scratching my head as to why it's not. I thought it was something to do with pass by value but I don't think it is. What am I doing wrong here?

Yes, pass-by-value is your culprit. You are passing a pointer by value.
Suppose l in your main() is at address 0xABCD. Then your main() gets compiled to
int main(void) {
List l;
initialize(0xABCD);
add(0xABCD, 2);
}
and your initialize() call looks like this (suppose malloc() succeeds and allocates memory at address 0xCDEF:
void initialize(List *l) {
if(l != 0x0) {
l = 0xCDEF; // malloc()
l->head = 0x0;
}
}
That l = 0xCDEF does not propagate to main(), because l was passed by value.
What you want to do is
void initialize(List **l) {
if(l != NULL) {
*l = malloc(sizeof(List)); // note dereferencing the passed-by-value pointer
(*l)->head = NULL;
}
}
int main(void) {
List * l;
initialize(&l);
add(l, 2);
}
which will pass pointer to pointer to list (actually the address of the pointer in your main(). It allows the code in initialize() to change the l variable in main().
Alternatively, you can use
List * list_init() {
List * retval = malloc(sizeof(List));
if(retval == NULL) { // you should check malloc return value
// abort(), print warning or just
return NULL;
}
retval->head = NULL;
return retval;
}
int main(void) {
List * l = list_init();
if(l == NULL) {
// handle the error
}
add(l, 2);
}

You declare a List in main() that lives on the stack. You pass a pointer to that List to initailize(). You then create a new List on the heap. When you return from initialize() you still are using the List on the stack that you had in the beginning. The List on the heap is leaked and you cannot access it. So you never initialized the List you pass as a pointer to add(). You can forget about initialize() and just have
l.head = NULL;
instead.

Did you code compile this line l->malloc(sizeof(list)); seems odd.
Create a structure with only one argument is not really useful, a simple typedef should do the job : typedef Node* List

Related

C Programming Error , printing linked list, at run time executed code crashes

I'm working on linked lists and pointers. Here is a simple code including a push function. After pushing my elements, and trying to print the first member, the executed code crashes at run time. However, when passing the same pointer to the print_list function and applying the printf function inside print_list it works fine. But when using it directly in the main function and applying the printf function it crashes.
#include<stdio.h>
#include<stdlib.h>
typedef struct list{
int order;
struct list *next;
}list;
void push(struct list **arg,int i);
int main()
{
struct list **ptr=NULL;
for(int i=0;i<10;++i){
push(&ptr,i);
}
print_list(&ptr); // Here works fine
printf("%d\n", (*ptr)->order); //Here run time error
return 0;
}
void push(struct list **arg,int i){
struct list *temp;
temp= malloc(sizeof(list));
temp->order=i;
temp->next=*arg;
*arg=temp;
}
void print_list(list ** head) {
while ((*head) != NULL) {
printf("%d\n", (*head)->order); //Here works fine too !
*head = (*head)->next;
}
}
There are a couple pointer management errors in this piece of code.
void print_list(list ** head) {
while ((*head) != NULL) {
printf("%d\n", (*head)->order);
*head = (*head)->next; // print_list is clearly a readonly function, you don't want to modify head of the list
}
}
Use iterator instead:
void print_list(list *head) { // Passing a copy of head pointer here, so real head can't be changed.
struct list *iter = head;
while (iter != NULL) {
printf("%d\n", iter->order);
iter = iter->next;
}
}
struct list **ptr=NULL; - Your want to declare pointer to head of the list here, but you are declaring pointer to this pointer.
Change it to: struct list *ptr = NULL;
And after this changes you don't need to either take an address of head to pass it to print_list:
print_list(&ptr) --> print_list(ptr)
or dereference it in printf:
printf("%d\n", (*ptr)->order) --> printf("%d\n", ptr->order)

How to make a linked list (pointer to struct) - c [duplicate]

This question already has answers here:
Explanation of code (linked list C)
(7 answers)
Closed 6 years ago.
Been given some functions, but cant seem to get main method working (the master list). What i thought would happen is you 1 master list and insert_at_front would add to it, but it only prints out the first list (10). Anyone know how i can get a linked list going? Thanks in advance :)
#include <stdlib.h>
#include "week1.h"
void insert_at_front(List *self, int data)
{
List newNode = (List)malloc(sizeof(struct node));
newNode->data = data;
newNode->next = *self;
*self = newNode;
}
void print_list(List *self)
{
List current = *self;
while (current != NULL)
{
printf("%d\n", current->data);
current = current->next;
}
printf("\n");
}
int main(void)
{
List *master;
insert_at_front(&master, 10);
insert_at_front(&master, 20);
print_list(&master);
return 0;
}
header:
typedef struct node
{
int data;
struct node *next;
} *List;
void print_list(List *self);
void insert_at_front(List *self, int data);
You typedefed List as a pointer to your struct node so the declaration of List *master is actually a pointer to a ponter to a node. When getting the address of master (&master) your getting a pointer to a pointer to a pointer to a node. Not quite what you want :)
You need to change the declaration of master to a pointer to a node and then getting the address of it
List master; // before: List* master
insert_at_front(&master, 10);
insert_at_front(&master, 20);
print_list(&master);
Edit:
Also include <stdio.h> for using printf.
At the moment you're also creating a memory leak since you're allocating memory by calling malloc but never calling free.
Usually the best thing you can do is to write a cleanup function for freeing memory right after you wrote something which allocated memory in the first place. A cleanup could look like this:
void delete_list(List* self)
{
while ((*self)->next)
{
List tmp = *self;
List last;
while ( tmp->next != NULL)
{
last = tmp;
tmp = tmp->next;
}
free(last->next); // delete the last node in the list
last->next = NULL;
}
free(*self); // now delete the only existing node
}

Recursive Linked List Reversal Function in C

My List is implemented via these two structs. The first contains the items in the list while the second contains the list itself.
typedef Employee Item;
typedef struct ListNodeTag {
Item item;
struct ListNodeTag *next;
} ListNode;
typedef struct {
int size;
ListNode *first;
} List;
I am trying to use the following recursive function to reverse the contents of the list however, I am getting a segmentation fault once there is more than one item in the list.
void Reverse(List *L){
ListNode *q,*p;
q = L->first;
p = q->next;
if(p == NULL)
return;
Reverse(L);
q->next->next = q;
q->next = NULL;}
I believe that the issue lies in the fact that instead of passing a member of the list as a function argument I am passing a pointer to the list itself. How would I change this code to let it work without passing a different argument?
You need to pass another parameter to the function to advance the recursive function to the end of the list. This can be done like this-
void Reverse(ListNode *f, List *l){
if(l->first == NULL)
return;
//Last node reached
if(f->next==NULL){
l->first->next = NULL;
l->first = f;
return;
}
ListNode *p,*q;
p = f;
q = f->next;
Reverse(f->next,l);
q->next = p;
}
Though this function works, it takes a lot of memory, so I'd recommend an iterative approach, like this one-
void Reverse(List *l){
ListNode *f = l->first;
ListNode *fn,*fnn;
if(f==NULL)
return;
fn = f->next;
if(fn==NULL)
return;
fnn = fn->next;
while(fnn!=NULL){
fn->next = f;
f = fn;
fn = fnn;
fnn = fnn->next;
}
fn->next = f;
l->first->next = NULL;
l->first = fn;
}
Have you made sure to initialise all your next pointers to NULL as you append items to the list?
Also, as I read it, the function won't actually recurse as you're always passing L. In other words, after the first call, how does the function know to go one further step down the list?

Add an element to a single linked list

typedef struct list
{
struct list * next;
int val;
}*list_t;
list_t add(list_t l,int e)
{
list_t head;
if(l == NULL)
{
l = malloc(sizeof(list_t));
l->val = e;
l->next = NULL;
return l;
}
head = l;
while(l != NULL)
l=l->next;
l = malloc(sizeof(list_t));
l->val = e;
l->next = NULL;
return head;
}
Sample driver:
int main()
{
list_t ints=NULL;
int i;
for(i=0;i<156;i+=2)
ints = add(ints,i);
while(ints->next != NULL)
{
printf("%d\n",ints->val);
ints=ints->next;
}
system("pause");
return 0;
}
Program works, but "add" function rewinds the list so that the body of main's loop is never achieved.It surprised me a lot, because I thought that I'd been passing list as a value! Could you explain this phenomenom?
The problem is not that the add function rewinds the list, is that it's not working at all: Nowhere in your code are you stating that the previous end of the list should link to the newly added element.
I've modified it slightly:
typedef struct list
{
struct list * next;
int val;
} list_t;
list_t *add(list_t *l,int e)
{
list_t *head;
if(l == NULL)
{
l = malloc(sizeof(list_t));
l->val = e;
l->next = NULL;
return l;
}
head = l;
while(l->next != NULL)
l=l->next;
l->next = malloc(sizeof(list_t));
l=l->next;
l->val = e;
l->next = NULL;
return head;
}
int main()
{
list_t *ints=NULL;
int i;
for(i=0;i<156;i+=2)
ints = add(ints,i);
while(ints->next != NULL)
{
printf("%d\n",ints->val);
ints=ints->next;
}
return 0;
}
The code now works as expected.
Remember that l is a local variable in the add function. Any changes made to l will be lost if it's not allowed to leave the scope of the function somehow (like you do when you return it, inside the first if). Changes made to the variable l points to, using either the * or the -> operators, will be effective to whoever has access to that variable.
I recomend that you start reading on debugging techniques. They vary depending on your environment, and can go from cryptic commandline tools like gdb to full-fledged graphical object browsers and such. This way you will be able to see what happens step by step and monitor memory changes and check what's really being stored in your variables.
EDIT: Fixed pointer trouble as commented. Memory allocations now provide for the whole struct variable, and pointers are no longer used implicitly.
Avoid the special cases. The add() function can do only one thing: allocate a list node and assign its pointer value to the first node in the chain that happens to be null. There is no difference between a NULL node at the head of the chain, in the middle, or at the tail. (of course null nodes cannot exist in the middle of the list. They can exist at the head of the list, but then the list would be empty) Find the first NULL and put the fresh node there.
struct list *add(struct list *lp, int e)
{
struct list **pp;
for (pp= &lp; *pp; pp = &(*pp)->next) {;}
*pp = malloc(sizeof **pp);
(*pp)->val = e;
(*pp)->next = NULL;
return lp;
}
l = malloc(sizeof(list_t));
that allocate a pointer to the struct, not the struct itself.
for example, on a 64 bit machine, the malloced size is 8, but it's supposed to be 16.
when you subsequently say
l->val = ..
l->next = ..
only God knows where you are writing to..
Go search some sample code of linked list, and read it through, I mean in the debugger.

Problem with pointers in C

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;

Resources