Creating and Understanding linked lists of structs in C - c
I am having trouble grasping the concepts of struct and the linked list data structure together. For example lets say we have have this code: a struct that has a worker's content and a linked list of these structs that contains nodes of each worker and a pointer to the next node(?).
typedef struct Schedule {
char name[10];
char description[10];
int hours;
int workordernum;
} Work;
typedef struct linkedlist {
struct Schedule work;
struct linkedlist *next;
} Node;
The problem is how do you make a method that always adds a node in the beginning of the list, a method that adds it anywhere(middle) in the list using user defined workordernum, and a method that always puts it in the end.
I am not quite understanding -> and * usages properly. I did read online about creating head and tail nodes but I didn't quite get its usage properly, since they had a struct for a list and a struct for a node.
Another thing which I didn't get is, lets say adding a node to the beginning of the list works, how do you go about then changing every single workordernum value for all the nodes that were previously there?
I understand one must keep track of every time a node gets added, removed, or moved, meaning every time these methods are called we must have a variable that keeps track of the number. So if we have a node in the list all ready, its order would be one, then we add another one to the beginning, how would we change the order number 1 to 2 and the one being added to 1?
Or how does node->next->next->next work if we only have one pointer? Then how would we print all of them? Since we cannot use a for loop.
So these are the concepts I cant grasp code wise. I would KINDLY appreciate it if you take your time to explain it rather than just give the code, if possible. Because I would have to apply what I learn to moving around and deleting nodes as well. I want to learn it on my own. If something must be given as a code example then that's fine but please just don't post all the answer codes for me.
-Thank You
*Please forgive any format errors since I am new to this site.
Edit: I do understand a pointer is an address and that -> pertains to "pointing to" a member. I mean I understand all the basics, but my understanding isn't firm enough else I could be doing what I need help with.
Edit2: I will try and make a head node with linked list from what I have learned so far. I will be using the structs above and it will be loose code, not perfect. This is just to make sure I'm on the right track so far.
int main() {
// creates a work struct to hold user content
Work *workstruct = (Work*)malloc((sizeof(struct Schedule));
// creates first node to hold a work struct for linkedlist
Node *initialNode = (Node*)malloc((sizeof(struct linkedlist));
// Method call to add work nodes to list in main
addWork(initialNode, workstruct);
}
void addWork(Node *initialNode, Work *workstruct) {
// Suppose user already initialized workstruct
// create double-pointer to make initialNode as head
Node **head = (Node **)malloc(sizeof(struct linkedlist));
// assigns user's workstruct to the workstruct of initialNode
initialNode->work = *workstruct;
// If im understanding what you have taught me so far,
// this is how you always add a node on the head
initialNode->next = *head;
*head = initialNode;
}
The only issue I seem to have so far is that every time I try and add a new node to the list, it makes the new node the head but loses the previous node that was in the list.
Linked Lists - 101 - Singly-linked Lists
This is a long answer. The reason I have gone into so much detail is that there are a large number of linked-list questions that I hope to answer in on place, with proper context.
When I learned C, I had a hard time with pointers. However, after implementing a linked list, I finally started to grasp the concept of pointers. Master linked lists is a good thing in C, and it will help you be comfortable with pointers. When things seem confusing, grab a pencil and paper and sketch out a diagram of a list and the associated links to nodes. I occasionally do that when I am working with complex list implementations.
A linked list is a way to store data records. Unlike an array where all elements occupy one contiguous memory block of memory, linked list elements occupy random fragments of memory.
There are two basic types of linked list; a singly-linked list, and a doubly-linked list. The difference is that a singly-linked list can only be traversed in one direction; while a doubly-linked list can be traversed in both directions.
A singly-linked list is accessed via its "head" pointer, or a pointer to the head list node. A doubly-linked list may also be accessed by via its "head" pointer, or via its "tail" pointer.
Unlike an array where each element of the array can be directly addressed by its array index, linked list elements are accessed sequentially.
Here is a layout of a singly-linked list:
Node #1 Node #2 Node #3 EndOfList
---------- ---------- -------- ---------
HEADPTR--> NEXTPTR--> NEXTPTR--> NEXTPTR--> NULL
DataPayload DataPayload DataPayload
Each node in the list, with its data payload, is separately allocated. A node structure (in C) might look something like this:
typedef struct NODE_PAYLOAD_S
{
/* Data Payload (defined by coder) */
char name[10];
char desc[10];
int hours;
int workordernum;
} NODE_PAYLOAD_T;
typedef struct LIST_NODE_S
{
/* Next-node pointer */
struct LIST_NODE_S *next; /* pointer to the next node in the list. */
NODE_PAYLOAD_T payload; /* Data Payload (defined by coder) */
} LIST_NODE_T;
To initialize a singly-linked list of the above structure:
LIST_NODE_T *listHead = NULL;
'listHead' is now a pointer to a linked list (with no nodes).
Here is how to add a new node at the head of the this list:
int LIST_InsertHeadNode(
LIST_NODE_T **IO_head,
Q: Why is a "double-pointer" required here (ie: LIST_NODE_T **...)? Why not use a "single-level' pointer (ie: LIST_NODE_T *...)?
A: A "single" pointer to the list head would not be sufficient for this operation. Specifically, this operation designates a new "head node". This means that the this function needs to modify the pointer that points to the head node.
BEFORE:
Node #Y Node #Z EndOfList
---------- ---------- ---------
HEADPTR--> NEXTPTR--> NEXTPTR--> NULL
DataPayload DataPayload
AFTER:
New Node Node #Y Node #Z EndOfList
---------- ---------- -------- ---------
HEADPTR--> NEXTPTR--> NEXTPTR--> NEXTPTR--> NULL
DataPayload DataPayload DataPayload
Note that before, HEADPTR was pointing to 'Node #Y'; then after, HEADPTR is pointing at 'New node'. When this function is called, the address of the listHead pointer is passed in, allowing this function to change where the listHead pointer is pointing. In otherwords, the address of the listHead pointer is being passed into this function, which is represented (internally to this function) as a pointer to the listHead pointer (a pointer to a pointer). That is why it is a "double-pointer".
char *I__name,
char *I__desc,
int I__hours,
int I__workordernum
)
{
int rCode=0;
LIST_NODE_T *newNode = NULL;
/* Allocate memory for new node (with its payload). */
newNode=malloc(sizeof(*newNode));
if(NULL == newNode)
{
rCode=ENOMEM; /* ENOMEM is defined in errno.h */
fprintf(stderr, "malloc() failed.\n");
goto CLEANUP;
Q: What's this 'goto CLEANUP;' business?
A: The C language, unlike C++ and JAVA, has no concept of an 'exception'. Error checking in C is critical. There are a number of reasons that the malloc() function might fail, and if it does, the code has to handle it as gracefully as possible. The 'goto CLEANUP' statement causes the normal program flow to skip code jumping to the 'CLEANUP:' label (within this function, below).
Obviously, if malloc() has failed, it would be unwise to try to initialize the NULL pointer (returned by the failed malloc) with the lines that immediately follow. So, it is important to divert the flow of the program to skip this initialization (and the linkage that comes later).
There is nothing special about the 'CLEANUP:' label. I could have called it 'ERROR:', 'EXIT:', 'FINISH:', 'LIAHONA:', 'MY_BAD' or anything else that suited my pleasure. (Labels don't have to be uppercase, nor do they have to be placed at the left margin. However, my style is to do that so that they stand out.)
Labels, such as 'CLEANUP:' has a scope that is limited to the boundaries of the function where they are placed; which allows each function to have a unique 'CLEANUP:' label (if needed).
}
/* Initialize the new node's payload. */
snprintf(newNode->payload.name, sizeof(newNode->payload.name), "%s", I__name);
snprintf(newNode->payload.desc, sizeof(newNode->payload.desc), "%s", I__desc);
newNode->payload.hours = I__hours;
newNode->payload.workordernum = I__workordernum;
/* Link this node into the list as the new head node. */
newNode->next = *IO_head;
*IO_head = newNode;
CLEANUP:
return(rCode);
}
The above function might be called as follows:
#include <stdio.h>
#include <errno.h>
int LIST_InsertHeadNode(LIST_NODE_T **, char *, char *, int, int);
int main(void)
{
int rCode=0;
LIST_NODE_T *listHead = NULL;
rCode=LIST_InsertHeadNode(&listHead, "Mahonri", "Jareds Bro", 4, 2421);
if(rCode)
{
fprintf(stderr, "LIST_InsertHeadNode() reports: %d\n", rCode);
goto CLEANUP;
}
CLEANUP:
return(rCode);
}
The LIST_InsertHeadNode() function may be called multiple times. Each call will add a new node to the list. The new node will be placed at the "head" of the list, which has the effect of pushing the rest of the nodes further down the list.
After adding several nodes to the list, it might be good to access the list; perhaps to print the payload of each node:
int PrintListPayloads(
LIST_NODE_T *head;
)
{
int rCode=0;
LIST_NODE_T *cur = head
int nodeCnt=0;
while(cur)
{
++nodeCnt;
printf("%s, %s, %d, %d\n",
cur->payload.name,
cur->payload.desc,
cur->payload.hours,
cur->payload.workordernum
);
cur=cur->next;
}
printf("%d nodes printed.\n", nodeCnt);
return(rCode);
}
The above function can be called from main():
#include <stdio.h>
#include <errno.h>
int LIST_InsertHeadNode(LIST_NODE_T **, char *, char *, int, int);
int PrintListPayloads(LIST_NODE_T *);
int main(void)
{
int rCode=0;
LIST_NODE_T *listHead = NULL;
/* Insert a linked-list node. */
rCode=LIST_InsertHeadNode(&listHead, "Mahonri", "Jareds Bro", 4, 2421);
if(rCode)
{
fprintf(stderr, "LIST_InsertHeadNode() reports: %d\n", rCode);
goto CLEANUP;
}
/* Insert a linked-list node. */
rCode=LIST_InsertHeadNode(&listHead, "Joe", "CEO", 5, 2419);
if(rCode)
{
fprintf(stderr, "LIST_InsertHeadNode() reports: %d\n", rCode);
goto CLEANUP;
}
/* Insert a linked-list node. */
rCode=LIST_InsertHeadNode(&listHead, "Eve", "Mother", 24, 2);
if(rCode)
{
fprintf(stderr, "LIST_InsertHeadNode() reports: %d\n", rCode);
goto CLEANUP;
}
rCode=PrintListPayloads(listHerad);
if(rCode)
{
fprintf(stderr, "PrintListPayloads() reports: %d\n", rCode);
goto CLEANUP;
}
CLEANUP:
return(rCode);
}
Adding nodes at the head of a list [ie: LIST_InsertHeadNode()] is one way to add nodes. However, there are times where adding nodes to the other end of the list (ie: the list 'tail') is preferable. The code below shows how this is done.
First, a function that will return the current 'tail node' of a list.
int LIST_GetTailNode(
LIST_NODE_T *I__listHead, /* The caller supplied list head pointer. */
LIST_NODE_T **_O_listTail /* The function sets the callers pointer to the
last node. */
)
{
int rCode=0;
LIST_NODE_T *curNode = I__listHead;
/* Iterate through all list nodes until the last node is found. */
/* The last node's 'next' field, which is always NULL. */
if(curNode)
{
while(curNode->next)
curNode=curNode->next;
}
/* Set the caller's pointer to point to the last (ie: tail) node. */
if(_O_listTail)
*_O_listTail = curNode;
return(rCode);
}
Next, a function that will insert a node at the tail of the list.
int LIST_InsertTailNode(
LIST_NODE_T **IO_head,
char *I__name,
char *I__desc,
int I__hours,
int I__workordernum
)
{
int rCode=0;
LIST_NODE_T *tailNode;
LIST_NODE_T *newNode = NULL;
/* Get a pointer to the last node in the list. */
rCode=LIST_GetTailNode(*IO_head, &tailNode);
if(rCode)
{
fprintf(stderr, "LIST_GetTailNode() reports: %d\n", rCode);
goto CLEANUP;
}
Important note: The LIST_GetTailNode() function will set the tailNode pointer to the last node in the linked list; -unless- there are no nodes in the list. When the list is empty, LIST_GetTailNode() will set the tailNode pointer to NULL.
/* Allocate memory for new node (with its payload). */
newNode=malloc(sizeof(*newNode));
if(NULL == newNode)
{
rCode=ENOMEM; /* ENOMEM is defined in errno.h */
fprintf(stderr, "malloc() failed.\n");
goto CLEANUP;
}
/* Initialize the new node's payload. */
snprintf(newNode->payload.name, sizeof(newNode->payload.name), "%s", I__name);
snprintf(newNode->payload.desc, sizeof(newNode->payload.desc), "%s", I__desc);
newNode->payload.hours = I__hours;
newNode->payload.workordernum = I__workordernum;
/* Link this node into the list as the new tail node. */
newNode->next = NULL;
if(tailNode)
tailNode->next = newNode;
else
This 'else' case indicates occurs when tailNode is NULL, meaning that (currently) the linked list has no nodes. In this case, this node will be the first (head) node in the list (as well as the last). So, the caller's 'List Head' pointer is updated to indicate that this new node is now the head node.
*IO_head = newNode;
CLEANUP:
return(rCode);
}
The LIST_InsertTailNode() function is called the same way LIST_InsertHeadNode() is called. The only difference is that with LIST_InsertTailNode(), the new node is inserted at the list's tail, rather than at the list's head.
OK, so now you can insert a new node at the head, or tail of the list. What about inserting a new node somewhere in the middle of the list?
For example, assume that you want to have a list where all the nodes are sorted by some field in the payload like 'name'. While it is possible to add all the nodes, and then sort the list afterwords; it is much easier to insert each new node into the list in it's proper place. Doing that, the list will always be maintained in sorted order automatically.
Accomplishing this is done in two steps. First, allocate and initialize the new node. Then figure out where its proper place is in the list, then link the new node into the list at that location.
First, a function that will return what will be the 'parent node' to the new node. (This node assumes that the list is being maintained in sorted order by name):
int LIST_FetchParentNodeByName(
LIST_NODE_T *I__head,
const char *I__name,
LIST_NODE_T **_O_parent
)
{
int rCode=0;
LIST_NODE_T *parent = NULL;
LIST_NODE_T *curNode = I__head;
/* Inform the caller of an 'empty list' condition. */
if(NULL == I__head)
{
rCode=ENOENT;
goto CLEANUP;
}
/* Find a node with a payload->name string greater than the I__name string */
while(curNode)
{
if(strcmp(curNode->payload.name, I__name) > 0)
break;
parent = curNode; /* Remember this node. It is the parent of the next node. */
curNode=curNode->next; /* On to the next node. */
}
/* Set the caller's 'parent' pointer. */
if(_O_parent)
*_O_parent = parent;
CLEANUP:
return(rCode);
}
And now, a function that will insert new nodes, keeping the list sorted by name.
int LIST_InsertNodeByName(
LIST_NODE_T **IO_head,
char *I__name,
char *I__desc,
int I__hours,
int I__workordernum
)
{
int rCode=0;
LIST_NODE_T *parent;
LIST_NODE_T *newNode = NULL;
/* Allocate memory for new node (with its payload). */
newNode=malloc(sizeof(*newNode));
if(NULL == newNode)
{
rCode=ENOMEM; /* ENOMEM is defined in errno.h */
fprintf(stderr, "malloc() failed.\n");
goto CLEANUP;
}
/* Initialize the new node's payload. */
snprintf(newNode->payload.name, sizeof(newNode->payload.name), "%s", I__name);
snprintf(newNode->payload.desc, sizeof(newNode->payload.desc), "%s", I__desc);
newNode->payload.hours = I__hours;
newNode->payload.workordernum = I__workordernum;
/* Find the proper place to link this node */
rCode=LIST_FetchParentNodeByName(*IO_head, I__name, &parent);
switch(rCode)
{
case 0:
break;
case ENOENT:
/* Handle empty list condition */
newNode->next = NULL;
*IO_head = newNode;
rCode=0;
goto CLEANUP;
default:
fprintf(stderr, "LIST_FetchParentNodeByName() reports: %d\n", rCode);
goto CLEANUP;
}
Important note: The LIST_FetchParentNodeByName() function will set the parent pointer to the node in the list that is immediately less than the specified I__name; -unless- the head node is greater than the specified I__name. For this special case, LIST_FetchParentNodeByName() will set the parent pointer to NULL.
/* Handle the case where all current list nodes are greater than the new node. */
/* (Where the new node will become the new list head.) */
if(NULL == parent)
{
newNode->next = *IO_head;
*IO_head = newNode;
goto CLEANUP;
}
/* Final case, insert the new node just after the parent node. */
newNode->next = parent->next;
parent->next = newNode;
CLEANUP:
return(rCode);
}
The LIST_InsertNodeByName() function is called the same way LIST_InsertHeadNode() or LIST_InsertTailNode() is called. The only difference is that with LIST_InsertNodeByName(), the new node is inserted into its sorted (by name) location in the list; rather than at the list head or tail.
There will be occasions when a node will have to be deleted from the list. This is done by locating the node to be deleted, unlinking the node from the list, and then deleting the node and it's payload.
First, a function to locate a specific node by the payload name field.
int LIST_FetchNodeByName(
LIST_NODE_T *I__head,
const char *I__name,
LIST_NODE_T **_O_node,
LIST_NODE_T **_O_parent
)
{
int rCode=0;
LIST_NODE_T *parent = NULL;
LIST_NODE_T *curNode = I__head;
/* Search the list for a matching payload name. */
while(curNode)
{
if(0 == strcmp(curNode->payload.name, I__name))
break;
parent = curNode; /* Remember this node; it will be the parent of the next. */
curNode=curNode->next;
}
/* If no match is found, inform the caller. */
if(NULL == curNode)
{
rCode=ENOENT;
goto CLEANUP;
}
/* Return the matching node to the caller. */
if(_O_node)
*_O_node = curNode;
/* Return parent node to the caller. */
if(_O_parent)
*_O_parent = parent;
CLEANUP:
return(rCode);
}
Here is a function that will delete a node from the list that matches a specific payload name.
int LIST_DeleteNodeByName(
LIST_NODE_T **IO_head,
char *I__name
)
{
int rCode=0;
LIST_NODE_T *parent;
LIST_NODE_T *delNode = NULL;
/* Find the node to delete. */
rCode=LIST_FetchNodeByName(*IO_head, I__name, &delNode, &parent);
switch(rCode)
{
case 0:
break;
case ENOENT:
fprintf(stderr, "Matching node not found.\n");
goto CLEANUP;
default:
fprintf(stderr, "LIST_FetchNodeByName() reports: %d\n", rCode);
goto CLEANUP;
}
Important note: The LIST_FetchNodeByName() function will set the parent pointer of the delNode; -unless- the the delNode is the head node. For this special case, LIST_FetchNodeByName() will set the parent pointer to NULL.
/* Unlink the delNode from the list. */
if(NULL == parent)
*IO_head = delNode->next;
else
parent->next = delNode->next;
/* Free the delNode and its payload. */
free(delNode);
CLEANUP:
return(rCode);
}
NOTE: All code above has been tested and should be functional, and can be downloaded as: 23279119_List_101.c
(To be continued- as per request...)
Related
Sentinel Node in a C Linked List
I'm trying to learn more about linked lists in C and I recently stumbled upon the Sentinel Node concept, but I can't wrap my head around it. According to the slides I have, the sentinel node should be the first thing on the list when it's created and the last when other nodes are added. There should be a pointer to permanently point to the Sentinel Node. All those stuff confuse me and I would love some help with the implementation. /*this is a simple LL implementation*/ #include <stdio.h> #include <stdlib.h> struct List { int data; struct List *next; }; void ListInsert(int new_data) { struct List *p; p = (struct List *)malloc(sizeof(struct List)); p->data = new_data; p->next = (head); head = p; } void printList(struct List *q) { q = head; while (q != NULL) { printf("%d ", q->data); q = q->next; } printf("\n"); } int main() { ListInsert(5); ListInsert(7); ListInsert(6); ListInsert(4); ListInsert(2); printList(head); return 0; } Now, if I want to create the sentinel node, how should I proceed?
According to the slides i have, the sentinel node should be the first thing on the list when its created and the last when other nodes are added.There should be a pointer to permanently point to the Sentinel Node. Let's start with the most important point: the purpose of a sentinel node, which is to mark the end of the list. There will not be real data associated with a sentinel node, so a list containing only a sentinel node is logically empty. A few things follow from that, including: the identity of the sentinel node is a property of the whole list, not of any (other) particular node list manipulation algorithms need to be written differently for linked lists whose ends are marked by a sentinel than for those whose ends are marked by some other means. each list need a place to store the sentinel's identity a list that is expected to have a sentinel is invalid if it does not have one There are many ways to implement the details, all with their own advantages and disadvantages. Personally, I would be inclined (in the non-sentinel case, too) to have a structure to represent an overall list, separate from the structure used to represent a list node. A pointer to the list's head node would be a member of this structure, and in the sentinel-terminated-list case, so would be a pointer to the sentinel node. When you create a new list, you create a sentinel node for it, too; initially, the list's head and sentinel pointers will both point to that node. The head pointer may be changed, but the sentinel pointer must not be. When you append to the list, the appended node gets placed just before the sentinel. It is an error to try to delete the sentinel from the list. It is to your advantage to write the code for this yourself.
Create it. You said "There should be a pointer to permanently point to the Sentinel Node", so create the pointer. Then use the pointer as the terminator of the list instead of NULL. Sentinel node - Wikipedia /*this is a simple LL implementation*/ #include <stdio.h> #include <stdlib.h> struct List { int data; struct List *next; }; struct List sentinel_node_instance; /* a pointer to permanently point to the Sentinel Node */ struct List* const SENTINEL_NODE = &sentinel_node_instance; /* the sentinel node should be the first thing on the list when it's created */ struct List* head = SENTINEL_NODE; void ListInsert(int new_data) { struct List *p; p = (struct List *)malloc(sizeof(struct List)); p->data = new_data; p->next = (head); head = p; } void printList(void) { struct List* q = head; while (q != SENTINEL_NODE) { printf("%d ", q->data); q = q->next; } printf("\n"); } int main() { ListInsert(5); ListInsert(7); ListInsert(6); ListInsert(4); ListInsert(2); printList(); return 0; }
Another variation of a sentinel node is for a circular doubly linked list, where it is both a head node and a sentinel node. Visual Studio implements std::list in this manner. head.next = pointer to first node or to head if empty list head.prev = pointer to last node or to head if empty list first.prev = pointer to head node last.next = pointer to head node
Segfault when accessing next node in singly linked list
I'm trying to just reverse a singly linked list, but with a bit of a twist. Rather than having the pointer to the next node be the actual next node, it points to the pointer in that next node. struct _Node { union { int n; char c; } val; void *ptr; /* points to ptr variable in next node, not beginning */ int var; }; typedef struct _Node Node; I know how to reverse a normal singly linked list and I think I have the general idea of how to go about solving this one, but I'm getting a segfault when I'm trying to access head->ptrand I don't know why. Node *reverse(Node *head) { Node * temp; Node * prev = NULL; while(head != NULL) { temp = head->ptr + 4; /* add 4 to pass union and get beginning of next node */ head->ptr = prev; prev = head; head = temp; } return prev; } Even if I try and access head->ptr without adding 4, I get a segfault. The driver that I have for this code is only an object file, so I can't see how things are being called or anything of the sort. I'm either missing something blatantly obvious or there is an issue in the driver.
First, I'll show you a major problem in your code: while (head) // is shorter than while(head != NULL) { // Where does the 4 come from? // And even if: You have to substract it. // so, definitively a bug: // temp = head->ptr + 4; /* add 4 to pass union and get beginning of next node */ size_t offset_ptr = (char*)head->ptr - (char*)head; // the line above should be moved out of the while loop. temp = head->ptr - offset_ptr; Anyways, your algorithm probably won't work as written. If you want to reverse stuff, you are gonna have to work backwards (which is non-trivial in single linked lists). There are two options: count the elements, allocate an array, remember the pointers in that array and then reassign the next pointers. create a temporary double linked list (actually you only need another single reversely linked list, because both lists together form a double linked list). Then walk again to copy the next pointer from your temporary list to the old list. Remember to free the temporary list prior to returning.
I tried your code and did some tweaking, well in my opinion your code had some logical error. Your pointers were overwritten again and again (jumping from one node to another and back: 1->2 , 2->1) which were leading to suspected memory leaks. Here, a working version of your code... Node *reverse(Node *head) { Node *temp = 0; //Re-ordering of your assignment statements while (head) //No need for explicit head != NULL { //Here this line ensures that pointers are not overwritten Node *next = (Node *)head->ptr; //Type casting from void * to Node * head->ptr = temp; temp = head; head = next; } return temp; }
Trying to create an empty linked list in C
I'm trying to create an empty linked list, which asks the user for the maximum number of terms that the list can hold. (I didn't add my code for that as its simply a printf). I then have to create a new function which asks the user to insert input into the previously created list. My question is, how do I make the create_q() function return the empty list? #include <stdio.h> #include <string.h> #include <stdlib.h> typedef struct node_t { int value; int priority; struct node_t *next; }node; typedef struct priority_linked_list { struct name *head; int current_size; int max_size; }priority_list; typedef node *Node; typedef priority_list *List; void create_q(int max_terms) { node *head = NULL; node *next = NULL; List *current_size = 0; List *max_size = max_terms; }
In C, linked lists are usually implemented as a series of nodes stored on the heap that point to eachother. The heap is a persistent memory area that runs throughout the life-cycle of the program. When you create a variable normally in a C function, and the function returns, the variable that you created is no longer accessible. However when you create something on the heap in a function, and the function is returned, the data you allocated on the heap is still there. However, you have no way of accessing it-- unless the function returns a pointer. So what you would do for create_q() would be to create the linked list on the heap (using a function in stdlib.h called "malloc"), and then you would return a pointer to your first node, letting the main function know where on the heap to find the first node. Then that first node would have a pointer in it, telling the program where on the heap to find the second node, and so forth. However, you're probably approaching linked lists the wrong way. Unless this is for some sort of homework project, you probably wouldn't want to create an empty linked list. One of the benefits of a linked list is that it's a dynamic structure in which you can easily insert new nodes. You could still have some variable keeping track of the maximum size you want the list to be, but you probably wouldn't want to actually create the nodes until you had to. Just keep in mind what a linked list is. It's a set of nodes floating on the heap (in C) that each store some data, and contain a pointer to the next node floating on the heap. All you need, to access the linked list, is a pointer to the first node. To add a new node, you simply "walk" through the list till you reach the last node, and then create a new node and have the old-last node point to it.
Is this what you had in mind? #include <stdio.h> #include <string.h> #include <stdlib.h> struct node_t { int value; int priority; struct node_t *next; }; static int current_size; static int max_size; static struct node_t* head = NULL; struct node_t* create_q(int); struct node_t* create_q(int max_terms) { int i; // loop counter/index current_size = max_terms; max_size = max_terms; if( NULL == (head = malloc(sizeof(struct node_t)*max_terms))) { // then, malloc failed perror("malloc failed for struct node_t list"); exit( EXIT_FAILURE ); } // implied else, malloc successful // set all fields to '0,0,Null' memset( head, 0x00, sizeof(struct node_t)*max_terms); // set all next links, except last link for(i=0;i<(max_terms-1);i++) { head[i].next = &head[i+1]; } // set last link head[i].next = NULL; return( head ); } // end function: create_q
I suspect you are looking for something like the following for creating or initializing your priority linked list. /***** * alloc_q - allocate memory for the priority linked list */ struct priority_linked_list *alloc_q(void) { struct priority_linked_list *list; list = malloc(sizeof(*list)); return list; } /****** * init_q - initialize the priority linked list */ void init_q(struct priority_linked_list *list, int max_terms) { list->head = NULL; list->current_size = 0; list->max_size = max_terms; } /****** * create_q - allocate AND initialize the priority linked list */ struct priority_linked_list *create_q(int max_terms) { struct priority_linked_list *list; list = alloc_q(); if (list == NULL) { return NULL; } init_q(list, max_terms); return list; } Allocation of nodes and their addition/removal to/from the list would be handled separately. There may be typos in the above (I have not tested it). However, it should be enough to get you on the path you want. Hope it helps.
What more does the code need to delete a node from a linked list successfully?
I want to delete a given node from a linked list by the node's index number (serial number). So what I tried to do in my function is that, first I have taken the user input of the index number. Then I used two node type pointers temp and current. I started traversing the list with current and when the index number of the node matches with the user input, I tried to delete the node. So far it is correct. I am facing problem with the deletion logic. Here is the code I tried: void delete_node(struct node **start,int index_no) { int counter=0; struct node *temp, *current; temp=(struct node *)malloc(sizeof(struct node)); current=(struct node *)malloc(sizeof(struct node)); current=*start; while(current->next!=NULL) { counter++; if(counter==index_no) { temp= current->next; free(current); /*I guess some code is missing here. Help me finding the logic.*/ } else { printf("\n The index number is invalid!!"); } } } The commented portion lacks the deletion logic. Also, I have a feeling that this code is not space and time-efficient. If it is so, please suggest to a way to make it more compact.
Why are you allocating two nodes in the delete function, then leaking their memory? It seems they should be initialized to start or one of its successors. You also need to update the next pointer in the previous element and potentially also the start (head) of the list if the removed element was the first (ie. index_no == 1). You also have an off-by-one error where the final node can never be deleted, because only a node with a ->next pointer will be considered for deletion. Suggested reading: A Tutorial on Pointers and Arrays in C.
Deleting from a linked list is actually: find the pointer that points to us (if found) make it point to our .next pointer instead delete our node. In order to change the pointer that points to us, we need a pointer to it: a pointer to pointer. Luckily the first argument already is a pointer to pointer, it presumably points to the head pointer that points to the first list item. struct node { struct node *next; int num; } ; void delete(struct node **pp, int num) { struct node *del; int counter; for (counter=0; *pp; pp= &(*pp)->next) { if(counter++ == num) break; } if (!*pp) { printf("Couldn't find the node(%d)\n", num); return; } /* if we get here, *pp points to the pointer that points to our current node */ del = *pp; *pp = del->next; free(del); }
Cons Cell data structure in C
I'm a newbie at C, in the early stages of building a small Scheme interpreter. For this part of the project I'm trying to build a simple cons cell data structure. It should take a list like (a b c) and represent it internally like so: [ ][ ] -> [ ][ ] -> [ ][/] | | | A B C To test that it's working correctly, I have a print function to to echo out the input. Here is the code that isn't working: #include <stdlib.h> #include <stdio.h> #include <string.h> #include "lexer.h" #include "parse.h" char token[20]; struct conscell { char *data; struct conscell *first, *rest; }; void S_Expression () { /* function from lexer to receive input a split into tokens no greater than 20 */ startTokens(20); /* gets the next token */ strcpy(token, getToken()); /* List is a typedef for the struct conscell */ List tree = createList (); tree = nextNode (tree); printList(tree); } List createList () { List node = malloc(sizeof (List)); if (node == NULL) { printf("Out of memory!\n"); exit(1); } node->data = NULL; node->first = NULL; node->rest = NULL; return node; } /* Recursive function to build cons cell structure */ List nextNode (List node) { node = createList (); if (token[0] == '(') { strcpy(token, getToken()); node->first = nextNode(node->first); node->rest = nextNode(node->rest); } else { if (token[0] == ')') { node = NULL; } else { List temp = createList(); temp->data = token; temp->first = NULL; temp->rest = NULL; node->first = temp; strcpy(token, getToken()); node->rest = nextNode(node->rest); } } return node; } /* Prints output. So far, just trying to print symbols */ void printList(List node) { if (node != NULL) { if (node->data != NULL) { printf("%s", node->data); } } } So far can't print out anything. I'm almost positive its a pointer issue. If anyone could point me (no pun intended) in the right direction, it'd be very much appreciated. Thank you
First, I'm assuming List is a typedef for a struct conscell*. If it's not, it should be, otherwise your code won't compile without tons of warnings. A scheme cons cell should be a simple singly linked list, not a doubly-linked list. So your individual cells should be more like: typedef conscell { unsigned char *data; //<== use unsigned char for a memory buffer struct conscell* next; //<== only a "next" pointer needed } conscell; I see you're just trying to print symbols at the moment, so using char rather than unsigned char can work for that purpose, but when you go with more generic data-structures like lambdas, etc., you're going to have to switch to either unsigned char* or void* for the reference to the memory buffer holding those types of more complex data-structures. The other issue that seems a bit confusing is that you're making each cell of your cons cells another cons cell, for instance, these lines of code, if (token[0] == '(') { strcpy(token, getToken()); node->first = nextNode(node->first); node->rest = nextNode(node->rest); } are recursively adding cons cells as your "first" and "rest" ... but that's not how a linked-list should look like. It should have a pointer to a list-node as the "head" of the list (not another cons-cell like it seems you're doing here), and then each node in the list points to some data and the next node in the list. Next, you have memory leaks all over the place with your createList() function as you allocate memory with it, but then never delete that memory (i.e., you have code like node = NULL which effectively is a memory leak because you've lost the memory reference to the allocated memory location that node was originally pointing to). You have to call free() on a node pointer before you assign NULL to it. Finally, printList() doesn't do anything but print the first element of the list you pass it ... there are no recursive calls or loops to cycle to the next node in the linked list. So you're not going to be printing much with that function. It should look more like: void printList(List node) { List current = node; while (current != NULL) //<== guard for the end-of-list { if (node->data != NULL) { printf("%s", node->data); } current = current->next; //cycle to the next node in the linked list } } So to sum things up, 1) your cons data-structure should represent a singly linked list composed of a structure data-type having a data element and a pointer to the next node. The cons'ed list is accessed through a head pointer pointing to the first node. 2) As you parse the input, you should add nodes to the front of the linked list since Scheme's cons operation, and really all the operations in scheme, are recursive, and "fold to the right", meaning they work from a base-case (i.e., the cons'ing of two elements), and then expand on that base-case. So if you had something like (cons 'd (cons 'c (cons 'b (cons 'a '())))), you'd the print the list (d c b a). If you want, it could also help to put tokens into a stack as your recursively parse the input, and then from the stack input into your linked list (sort of like how a RPN calculator would work).
Also add \n to your printf to make sure it is flushed to stdout: printf("%s\n", node->data);