I'm learning C and I'm trying to understand a better concept of a Linked List.
I have a code as an example (I changed it to be like the one i want to understand better).
The code doesnt have any mistakes but again like i said it's to understand it better.
Please have a look at the code first and below the code i'll ask my questions:
Here is the code for my struct and my first function:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct client{
char first_name[20];
char last_name[20];
char id;
char reference;
char deposit;
struct client *next; // pointer to the next entry in the clients list
};
typedef struct client new_Client;
/***********************************************************
* find_Client
* Finds a place in the list to insert new user
* Input: User's first and last name, id, reference, deposit
* Returns: A pointer to teh struct which AFTER we'll insert
the new struct. NULL if head of the linked-list
***********************************************************/
new_Client *find_Client(char *new_last_name, new_Client *head)
{
new_Client *prev = NULL; // previous has nothing
new_Client *curr = head; // current is the head
while (curr != NULL) //// while the current is not NULL because if it's null it's not head
{
if (strcmp(new_last_name, curr->last_name)<0)
break;
prev = curr;
curr = curr->next; // pointing to the next node
}
return prev;
}
And here is my next function where i usually have a problem understanding
/***********************************************************
* add_new_Client
* Adds a new client
* Input: clients details and the address of the list's head
* Returns: nothing
***********************************************************/
void add_new_Client(char *first_n, char *last_n, char *ident, char *ref, char *dep, new_Client **head)
{
new_Client *after;
// create new entry
new_Client *new_entry = (new_Client*)malloc(sizeof(new_Client)); //allocating size of the struct
if (NULL == new_entry)
{
exit(1); //Q1: does it mean that it terminates?
}
else
{
strcpy(new_entry->first_name, first_n);
strcpy(new_entry->last_name, last_n);
strcpy(&new_entry->id, ident);
strcpy(&new_entry->reference, ref);
strcpy(&new_entry->deposit, dep);
}
after = find_Client(new_entry->last_name, *head); // Q2: don't understand this line..
if (NULL == after) // new head
{
if (NULL == *head) // Q3: for adding the first item (when the head is NULL) --> why is this condition necessary
{
new_entry->next = NULL;
}
else
{
new_entry->next = *head;
}
*head = new_entry;
}else
{ //add in the middle
new_entry->next = after->next;
after->next = new_entry;
}
}
Okay so i put my questions in the code itself. They're marked as Q1,Q2 and Q3.
Please let me know if you would prefer i edit on my post and change the questioning methode for it to be easier for you to understand my questions.
//Q1: does it mean that it terminates?
exit(1);
Yes, the program will exit with 1, which is interpreted as failure.
// Q2: don't understand this line..
after = find_Client(new_entry->last_name, *head);
here, after is a pointer to a struct new_Client. The function find_Client returns a pointer to a client that was searched using new_entry->last_name, and head is passed in order to give the search a beginning, somewhere to start.
// Q3: for adding the first item (when the head is NULL) --> why is this condition necessary
if (NULL == *head)
Because when the there is only one element in the list it is alone, that is, it doesn't have a next element. If the list size is higher than one, however, the new node's next element will point to the head, and the this new node will be the new head. That is, the new node's next element will have the value of the last head element, just before it. This is why this distinction is necessary.
Related
I'm still learning how to program in C and I've stumbled across a problem.
Using a char array, I need to create a linked list, but I don't know how to do it. I've searched online, but it seems very confusing. The char array is something like this char arr[3][2]={"1A","2B","3C"};
Have a look at this code below. It uses a Node struct and you can see how we iterate through the list, creating nodes, allocating memory, and adding them to the linked list. It is based of this GeeksForGeeks article, with a few modifications. I reccommend you compare the two to help understand what is going on.
#include <stdio.h>
#include <stdlib.h>
struct Node {
char value[2];
struct Node * next;
};
int main() {
char arr[3][2] = {"1A","2B","3C"};
struct Node * linked_list = NULL;
// Iterate over array
// We calculate the size of the array by using sizeof the whole array and dividing it by the sizeof the first element of the array
for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++) {
// We create a new node
struct Node * new_node = (struct Node *)malloc(sizeof(struct Node));
// Assign the value, you can't assign arrays so we do each char individually or use strcpy
new_node->value[0] = arr[i][0];
new_node->value[1] = arr[i][1];
// Set next node to NULL
new_node->next = NULL;
if (linked_list == NULL) {
// If the linked_list is empty, this is the first node, add it to the front
linked_list = new_node;
continue;
}
// Find the last node (where next is NULL) and set the next value to the newly created node
struct Node * last = linked_list;
while (last->next != NULL) {
last = last->next;
}
last->next = new_node;
}
// Iterate through our linked list printing each value
struct Node * pointer = linked_list;
while (pointer != NULL) {
printf("%s\n", pointer->value);
pointer = pointer->next;
}
return 0;
}
There are a few things the above code is missing, like checking if each malloc is successful, and freeing the allocated memory afterwards. This is only meant to give you something to build off of!
I'm having trouble writing a function that will delete a node on a generic linked list.
I have my linked list declared as follow (this is the way my professor wants us to do):
typedef enum _STATUS {ERROR,OK} STATUS;
typedef enum _BOOLEAN {FALSE, TRUE} BOOLEAN;
#define MAX_NOME 20
typedef struct _FUNC
{
char name[MAX_NOME];
char dept[MAX_NOME];
BOOLEAN permanent;
} FUNC;
typedef struct _LIST_NODE
{
void * data;
struct _LIST_NODE * next;
} LIST_NODE;
typedef LIST_NODE * LIST;
#define DATA(node) ((node)->data)
#define NEXT(node) ((node)->next)
I've come with this function to delete all nodes with permanent == FALSE, but it is really not working.
void DeleteFuncNotPermanent(LIST *list)
{
LIST *node = list;
while ((*list)->next != NULL)
{
if(((FUNC*)DATA(*list))->permament == FALSE)
{
node = list;
list = &(NEXT(*node));
free(DATA(*node));
free(*node);
}
else
{
list = NEXT(*list);
}
}
}
Any feedback would be greatly appreciated. Thank you.
You iterate through the list with a pointer to node pointer, which is a good idea. (Typecasting away the pointer nature of LIST is not a good idea, however.) There are several errors in yur code.
To get a pointer to the last element of a list, you do:
Node **p = &head;
while (*p) {
p = &(*p)->next;
}
Your respective code, i.e. your function without the deletion stuff, looks like this:
while ((*list)->next != NULL) {
list = NEXT(*list);
}
You should iterate while (*list). The idea to check next probably stems from similar code that uses a node pointer to iterate. When you use a pointer to node pointer, dereferencing that pointer has the same effect as accessing next, because that pointer points to the head node at first and to the previous node's next member on subsequent iterations.
That's why you must assign the address of (*list)->next to list when you want to advance the pointer. (The comiler warns you that the pointer types don't match.)
So the "raw" loop should be:
while (*list != NULL) {
list = &NEXT(*list);
}
Now let's look at deletion. When you have determined that the node should be deleted, you do:
LIST *node = list;
list = &(NEXT(*node));
free(DATA(*node));
free(*node);
Here, you do not want to advance the iterator pointer. Instead, you want to update what it points at: You want to skip the current node *list by deflecting the pointer that points to the node to the next node or to NULL, when that was the last node:
*list = NEXT(*node);
When you do that, list and node will still be the same address, only the contents have changed. (Because node == list, *node now points at the node after the node you want to delete and you accidentally free that node and its data. Make the temporary pointer a simple node pointer:
LIST node = *list;
*list = NEXT(node);
free(DATA(node));
free(node);
Putting it all together:
void DeleteFuncNotPermanent(LIST *list, int c)
{
while (*list)
{
if (((FUNC*) DATA(*list))->permament == FALSE)
{
LIST node = *list;
*list = (NEXT(*list));
free(DATA(node));
free(node);
}
else
{
list = &NEXT(*list);
}
}
}
I have tried to rewrite your delete function. Let me know if it works. The changes are basically related to pointer dereferencing.
void DeleteFuncNotPermanent(LIST *list)
{
LIST *node = list;
while (list!= NULL) // change
{
if(((FUNC*)(DATA(list)))->permament == FALSE) // change
{
node = list;
list = NEXT(node); //change
free((FUNC*)(DATA(node))); // change
free(node); // change
}
else
{
list = NEXT(list); //change
}
}
}
during working on the project to school, i find a little problem, which i do not know remove.
Problem is in fuction clear_train. When i trying to remove 1. element, function return also my element(but empty).
Here is my source code where are definitions of all functions:
(in a_train.h are a declarations and descriptions of functions)
#include "a_train.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct car* add_car(struct car* first,const char* target) {
struct car* adder = malloc(sizeof(struct car));
struct car *current = first;
strcpy (adder->value,target);
adder->next=NULL;
if(first == NULL){
first =adder;}
else{
while (current->next != NULL) {
current = current->next;
}
current->next = adder;
}
return first ;
}
void print_train(struct car* first) {
if (first!=NULL){
while (first!=NULL){
printf("%s\n",first->value);
first=first->next;
}
}
else printf("empty list\n");
}
void cancel_train(struct car* first) {
while(first!=NULL){
struct car* canceler = first->next;
free(first);
first=canceler;
}
}
struct car* clear_train(struct car* first, const char* target) {
if(first == NULL){
return NULL;
}else if (first->next==NULL){
if(strcmp(first->value,target)==0){
free(first);
return NULL;
}else return first;
}
struct car* prev_searcher =first;
struct car* this_searcher =first;
while (this_searcher!=NULL){
if(strcmp(this_searcher->value,target)==0){
prev_searcher->next=this_searcher->next;
free(this_searcher);
}
prev_searcher=this_searcher;
this_searcher=this_searcher->next;
}
return first;
}
here is the definition of linked list:
struct car {
char value[SIZE];
struct car* next;
};
source code of main where i am calling functions:
int main(){
struct car* train = NULL;
train = add_car(train,"Presov");
train = add_car(train,"Bratislava");
train = add_car(train,"Levoca");
train = add_car(train,"Spiska Nova Ves");
train = add_car(train,"Bardejov");
train = add_car(train,"Pichne");
clear_train(train,"Presov");
print_train(train);
cancel_train(train);
return 0;
}
And finally there is output:
//there is empty node
Bratislava
Levoca
Spiska Nova Ves
Bardejov
Pichne
You are making things more difficult to remove a node from a linked list than it needs to be. While you are free to return and assign the head node each time clear_train is called, that's not really the way to do it.
Instead of passing a pointer-to-head (first) as the parameter to clear_train, pass the actual address of the head pointer, e.g.
clear_train(&train,"Presov");
That way you can manipulate the value (node) at that address directly. If the first node in the list is the one being delete, you simply update the node address held by the original pointer to the new first node and you are done.
Instead of trying to keep a previous, current and next node, simply use a pointer-to-pointer to current node. Then when the node containing target is found, you simply update the pointer at that address to the ->next node and you are done. See Linus on Understand Pointers
That reduces your clear_train function to:
void clear_train (struct car **first, const char* target) {
struct car **ppn = first; /* pointer-to-pointer to first */
struct car *pn = *first; /* pointer-to-first */
for (; pn; ppn = &pn->next, pn = pn->next) { /* iterate, find target */
if (strcmp (pn->value, target) == 0) { /* if found */
*ppn = pn->next; /* set pointer at target address = next */
free (pn); /* free node contianing target */
break;
}
}
}
Much simpler than trying to test for different cases of where the node lies.
In clear_train
when you find the car - you free the element and then you try to get to it again so the program will make you problem.
write like this
if(strcmp(this_searcher->value,target)==0){
prev_searcher->next=this_searcher->next;
free(this_searcher);
return prev_searcher}
I try to use the CreateRoom function to adding new nodes.
Each time I add a node, I edit the old "lastRoom.next" and make it the current new node's address.
And then I make the current new node's pointer as the new "lastRoom"
I thought this is a good idea and I don't need to return anything. I thought this is good.
However, it doesn't work at all.
I'm really bad at coding and I just learn C. Can anyone help?
struct Room {
int number;
int status;
int luxury;
char occupier[20];
struct Room *next;
};
//I main copy this kind of code from my lecture notes.
//But It's really crappy. This is the only example I can get from the lecture notes
typedef struct Room Room_t;
Room_t *newRoomNode, *ptr, *prev, *temp;
Room_t *firstRoom = NULL, *lastRoom = NULL;
Room_t* CreateRoom(Room_t **first, Room_t **last, int RoomNumber, int type){
Room_t *new = (Room_t *)malloc(sizeof(Room_t));
int *temp;
if (new == NULL)
{
printf("\nMemory was not allocated");
return 0;
}
else
{
//And then I try my way of adding new nodes.
//I don't really understand the example so I make my own
if (*last != NULL)
{
(**last).next = new;
}
new->number = RoomNumber;
new->luxury = type;
new->next = NULL;
*last = new;
if (*first=NULL){
*first=new;
}
}
return 0;
}
void main(){
CreateRoom(&firstRoom, &lastRoom,1,1);
printf("%d",(*firstRoom).number);
}
if (*first=NULL){
*first=new;
}
= is assignment operator. You should use == for comparison.
You shouldn't really bother about the last element. (If you need to traverse the list backward, you have to have a prev member in addition to next.) Now, if you want CreateRoom() to always add a new element at the end of the list, it should first traverse the whole list until it reaches the end of it —which it recognizes because of the NULL pointer— and then assign the new pointer to the place it has reached:
while (*first != NULL)
first = &(*first)->next;
new->number = RoomNumber;
new->luxury = type;
new->next = *first;
*first = new;
Two points are worth being noted:
The assignment *first = new doesn't know if first is firstRoom or the next member of an actual element.
The while loop can be omitted to have new elements inserted at the beginning, or modified so as to have elements sorted the way you want.
I'm in the process of writing a shell for Linux for my Systems Programming class. I've hit a very weird situation in writing a linked list for my command history portion and I'm really not sure what I'm doing wrong.
Right now I have the program set up to accept a char array as input and add the data into a node at the end of a linked list and then set that node as the tail of the list. I then have the program print the list from the tail to head. What I should be seeing is the history of what I've typed in, in reverse order. What I am seeing is that what ever I've typed last appears to be overwriting the data in all of the previous nodes.
Eg: I enter 1, I get a 1 printed back, I then enter 2, I get two 2's printed back at me instead of 2 and then a 1. Does anyone know what is going on? My code is below.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define INPUT_BUFFER 255
typedef struct commandHistoryNode commandHistoryNode;
struct commandHistoryNode{
commandHistoryNode *previous;
commandHistoryNode *next;
char *commandLine;
};
commandHistoryNode* AddCommandToLinkedList(commandHistoryNode *, char *);
void PrintLinkedList();
void ExecuteCommand(char *);
int main(int argc, char **argv){
char *cmdString;
cmdString = calloc(INPUT_BUFFER, sizeof(char));
char *PS1 = "$";
commandHistoryNode *currentNode = NULL;
while(1)
{
printf(PS1);
fgets(cmdString, INPUT_BUFFER, stdin);
//currentNode is the tail of the LinkedList
currentNode = AddCommandToLinkedList(currentNode, cmdString);
PrintLinkedList(currentNode);
}
}
void ExecuteCommand(char *passedCommand){
return;
}
commandHistoryNode*
AddCommandToLinkedList(commandHistoryNode *passedNode, char *passedCommand){
commandHistoryNode *tailNode = malloc(sizeof(commandHistoryNode));
tailNode->commandLine = passedCommand;
if(passedNode == NULL)
{
//passedNode is the head of the list
return tailNode;
}
else
{
while(passedNode->next!=NULL)
{
//if passedNode isn't the tail (and it should be), iterate through the list
passedNode = passedNode->next;
}
//set tempNode to the next node for the passedNode
passedNode->next = tailNode;
//set the previous node for tempNode to point to the passedNode
//as it is the new tail.
tailNode->previous = passedNode;
}
//return new tailNode
return tailNode;
}
void PrintLinkedList(commandHistoryNode *tailNode){
while(tailNode != NULL)
{
printf("command is: %s\n", tailNode->commandLine);
//iterate backwards from the tail to the head
tailNode = tailNode->previous;
}
}
You keep re-using the same cmdString for each call to AddCommandToLinkedList. So every node points to the same character buffer. As a result, every time you have a new command, you overwrite the one and only character buffer with that command, and every node points to it. You need to allocate a cmdString for every command, instead of re-using it.
Check again what you return in case passedNode is != NULL.