I need to implement a LinkedList from a file .csv file. My node is defined like this:
struct NODE {
char username[50];
char password[50];
char usertype[50];
struct NODE *next;
} *head=NULL;
I have put my whole .csv file inside an 1D array called database. I then want to sscanf this array and put each corresponding word inside a particular field inside my node. I have the following code written down:
void createList() {
// I need a while-loop here //
struct NODE* toAdd = (struct NODE*)malloc(sizeof(struct NODE));
sscanf(database, "%[^','],%[^','],%[^'\n']", toAdd->username, toAdd->password, toAdd->usertype);
if (add(toAdd) == false) {
printf("Error: was unable to initialize password validation!");
exit(EXIT_FAILURE);
}
}
My add function looks like this:
BOOLEAN add(struct NODE *p) {
if (p == NULL) return false;
if (head == NULL) {
p->next = NULL;
head = p;
} else {
p->next = head;
head = p;
}
return true;
}
Is there something wrong with the functions above? To sum up, I want to read from an array. I "break" this array into three-word-block, then assign the first one to the username field of the node, the second one to the password field, etc. I want to do this on my whole array, therefore creating one big LinkedList of nodes.
Thank you for your help.
Related
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
}
}
}
Let "List" be a struct that represents a singly-linked list, i have the following function:
int List_contains(List node, const char* key) {
List *search;
search=node.next;
assert(search->key!=NULL);
return 1;
}
and List is the following struct:
typedef struct List { /*A new node*/
char* key;
void* value;
struct List *next;
} List;
The function "List_contains" should tell me if "key" is contained in the list or not. Problem is, i can't iterate through the list, and the line
assert(search->key != NULL);
throws a Segfault. How can i iterate through the list with what i have?
(Note: The function is, obviously, not "completed".)
Here is an example of a search I wrote a while back.
struct node
{
int data;
struct node *next;
};
int
search_list(struct node *head, int search)
{
if (!head)
{
fprintf(stderr, "Invalid head\n");
return 1;
}
struct node *temp = head;
while (temp)
{
if (temp->data == search)
{
printf("Found %d\n", search);
break;
}
temp = temp->next;
}
return 0;
}
int List_contains(List node, const char* key) {
List *search;
search=node.next;
assert(search->key!=NULL);
return 1;
}
so a lot to parse here...
Generally you would pass in a pointer to the node so that it isn't copied, but it is actually safe to do what you have done, in this instance, assuming no multithreaded shenanigans go on...
but in the future I would look for:
int List_contains(List* node, const char* key) {
next line you make a pointer without a value... which is fine, because you assign it the next line, but that could be condensed...
// List *search;
// search=node.next; # could easily become:
List *search = node.next;
now at this point you actually know if you are at the end of the list...
if(search == null)
{
return 0;
}
after that you need to do some sort of compare...
if (strcmp(key,search->key) == 0)
{
//found it;
return 1;
}
now you have code that will match the key against the first element of the list, so you would need to put the whole thing in a loop, each iteration swapping search with search->next and checking for null
So, I am reading from a file and inserting national park names into nodesin a double linked list. Now, I want to insert alphabetically, and I thought I was doing that in the insert function, but when I go to print out the list, it is just in the order in which they are inserted in. My guess is that I have an error in the strcmp in the insert function, but I can't figure it out.
I am suppose to use sentinel nodes. So, the first node has "" in it and the last node will have 177 in the data fields. This is my first time using sentinel nodes, and my teacher wanted us to use them, so I apologize if there is errors with them.
Thanks for the help.
#define DUMMY_TRAILER '\177'
typedef struct node NODE;
struct node
{
char data[20]; //for sentinel nodes
char parkName[20];
NODE *forw;
NODE *back;
};
FILE *Open(void); // Opens file
NODE *init_list(void); //Creates the sentinel nodes
void insert(NODE *list, char *name); //Inserts nodes into list
void traverse_forw(NODE *list); // Traverse the list and prints
Main
int _tmain(int argc, _TCHAR* argv[])
{
FILE* inputFile;
NODE *list;
char tempName[31];
inputFile = Open();
list = init_list();
while(fgets(tempName, 31, inputFile) != NULL)
{
insert(list, tempName);
FLUSH;
}
traverse_forw(list);
free(list);
return 0;
}
Insert Function
void insert(NODE *list, char *data)
{
NODE *curr = list->forw;
NODE *prev = list;
NODE *pnew;
int duplicate = 1;
// search
while (strcmp(data,curr->data) > 0)
{
prev = curr;
curr = curr->forw;
}
if (strcmp(data, curr->data))
{
duplicate = 0; // not a duplicate
pnew = (NODE *) malloc(sizeof (NODE));
if (!pnew)
{
printf("Fatal memory allocation error in insert!\n");
exit(3);
}
strcpy(pnew->parkName, data);
pnew->forw = curr;
pnew->back = prev;
prev->forw = pnew;
curr->back = pnew;
}
return;
}
Traverse to print out park names
void traverse_forw(NODE *list)
{
putchar('\n');
list = list->forw; // skip the dummy node
while (list->data[0] != DUMMY_TRAILER)
{
printf("%s\n", list->parkName);
list = list->forw;
}
return;
}
You are storing file read-lines into parkName but comparison is based on the data field
First of all you should create an empty linked list which is equal to NULL, then in the insert function before search, you control if the list is empty or not (in order to not comparing an string with NULL in the first insertion).
another issue is that in this part of the insert function:
while (strcmp(data,curr->data) > 0)
{
prev = curr;
curr = curr->forw;
}
you are only controlling if the data is alphabetically after the current data, I mean you control only forward and if you insert a data which starts with "D" after a data which starts with "R" for instance, your code won't handle that. so you should also consider backward control as well.
Another issue is which pouyan mentioned above, that you are putting data into the data and not in the parkname.
I have a linked list that contains two "strings", one for searching and one for replacing. I also have a text file that I'm supposed to open and read line by line then see if the words exist in the "dictionary" (the linked list) if it does, I have to replace it with the word's definition. Then write the changed text into a new text file, so I thought I should use a buffer when reading.
The problem is, I don't know how to iterate over the linked list. So far I have two words in it but it only searches for the first one in the loop:
char *textLine = NULL;
size_t textlen = 0;
ssize_t readText;
struct node *n = malloc(sizeof(*n));
n = head;
char buffer[MAX_L];
while ((readText = getline(&textLine, &textlen, t)) != -1) {
char *t = strtok(textLine, " ");
while (t != NULL) {
if (strcmp(t,n->word) == 0) {
// do something
} else {
// do something
}
n = head;
t = strtok(NULL, " ");
}
}
head is NULL, I guess that's why it only searches for the first word I just don't really know how should I iterate over both the lines and the linked list.
How, specifically, to iterate over a linked list depends, to some extent, on its interface.
Not having the interface to the particular implementation you are using available, the question is difficult to answer; but typically; a linked list looks something like this:
typedef struct list_node ListNode;
struct list_node {
void *payload;
ListNode *next;
}
Iterating is usually (always?) done by following the next pointer, so long as it is not NULL; like so:
void iterate (ListNode *head) {
while (head) {
if (interested_in_payload(head->payload)) {
// do stuff
}
head = head->next;
}
}
This:
struct node *n = malloc(sizeof(*n));
n = head;
looks very scary. It's almost never correct to first allocate some memory, then immediately overwrite the pointer.
Perhaps you meant
head = n;
?
This is something of a followup to a question I asked earlier. I'm still learning my way around pointers, and I'm finding it difficult to maintain a reference to the physical address of a struct while iterating through a data structure. For example, I have a simple, barebones linked list that I'd like to delete from via a searching pointer:
struct Node{
int value;
struct Node* next;
};
struct Node* createNode(int value){
struct Node* newNode = malloc(sizeof *newNode);
newNode->value = value;
newNode->next = NULL;
return newNode;
}
void nodeDelete(Node **killptr){
free(*killptr);
*killptr = NULL;
}
int main(){
struct Node* head = createNode(16);
head->next = createNode(25);
head->next->next = createNode(51);
head->next->next->next = createNode(5);
// Working code to delete a specific node with direct reference address
struct Node** killptr = &head->next;
nodeDelete(killptr);
return 0;
}
The above shows deleting by passing nodeDelete a pointer to the address of the head pointer. What I want to do is be able to move my pointer ->next until it finds something that satisfies a delete condition, and call nodeDelete on that. I've tried the following:
struct Node* searchAndDestroy = head;
while(searchAndDestroy->value != NULL){ // Search until the end of the structure
if (searchAndDestroy->value == 25){ // If the value == 25
nodeDelete(&searchAndDestroy); // Delete the node (FAILS: Nullifies the
// address of search variable, not the
break; // original node)
}else{
searchAndDestroy = searchAndDestroy->next;
}
}
I've also tried something along the lines of:
if (searchAndDestroy->value == 25){
struct Node** killptr = (Node**)searchAndDestroy);
nodeDelete(killptr); // Still fails
}
I need to be able to move my pointer to the ->next point, but also maintain a reference to the address of the node I want to delete (instead of a reference to the address of the search node itself).
EDIT: Some clarification: I realize that deleting from a linked list in this fashion is naive, leaks memory, and drops half the list improperly. The point is not to actually delete from a linked list. Ultimately the idea is to use it to delete the leaves of a binary search tree recursively. I just figured a linked list would be shorter to portray in the question as an example.
struct Node **searchAndDestroy;
for (searchAndDestroy = &head;*searchAndDestroy; searchAndDestroy = &(*searchAndDestroy)->next ){
if ((*searchAndDestroy)->value == 25){
nodeDelete(searchAndDestroy); // Function should be changed to assign the ->next pointer to the **pointer
break;
}
}
And change nodeDelete like this:
void nodeDelete(Node **killptr){
Node *sav;
if (!*killptr) return;
sav = (*killptr)->next;
free(*killptr);
*killptr = sav;
}
Unless I'm missing something, your nodeDelete function is working as designed, but you want to keep a way of accessing the next node in the chain. The easiest way of doing this is just to add a temporary variable:
struct Node *searchAndDestroy = head, *temp = NULL;
while(searchAndDestroy != NULL){ // Need to check if the node itself is null before
// dereferencing it to find 'value'
temp = searchAndDestroy->next;
if (searchAndDestroy->value == 25){
nodeDelete(&searchAndDestroy);
break;
}else{
searchAndDestroy = temp;
}
}
if you give the Address of the previous Node that is where the link to deleting node present then it is very simple
code snippet for that:-
void delete_direct (struct Node *prevNode)
{/*delete node but restrict this function to modify head .So except first node use this function*/
struct Node *temp;/*used for free the deleted memory*/
temp=prevNode->link;
prevNode->link=temp->link;
free(temp);
}
struct Node * find_prev(struct Node *trv_ptr,int ele)
{
/*if deleting element found at first node spl operation must be done*/
if(trv_ptr->data==ele)
return trv_ptr;
while((trv_ptr->link)&&(trv_ptr->link->data!=ele))
{
trv_ptr=trv_ptr->link;
}
if(trv_ptr->link==NULL)
{
return NULL;
}
else
return trv_ptr;
}
main()
{
/*finding Node by providing data*/
struct Node *d_link;
struct Node *temp;
d_link=find_prev(head,51);
if(d_link==NULL)
{//data ele not present in your list
printf("\nNOT FOUND\n");
}
else if(d_link==head)
{//found at first node so head is going to change
temp=head;
head=head->link;
free(temp)
}
else
{//other wise found in some where else so pass to function
delete_direct (d_link);
}
}