My assignment is to read a file in to a linked list. The file contains a name and a letter indicating what to do with the name, either add or delete from list. The file is in this format:
Kathy a
Beverly a
Chuck a
Radell a
Gary a
Roger d
and so on...
The problem comes when i try to split up the name and the operation. The strcmp() doesn't even recognize the op_code variable in my code. I printed out both the name and the op_code and they print right unless i put a character near the op_code.
Here's my code:
//Tristan Shepherd
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct node
{
char name[42];
struct node *next;
};
void printList(struct node *head)
{
struct node *current = head;
while (current)
{
printf("3 %s\n", current->name);
current = current->next;
}
}
void addFront(struct node **head, char *newname)
{
struct node* newnode = (struct node*)malloc(sizeof(struct node));
strcpy(newnode->name, newname);
newnode->next = (*head);
(*head) = newnode;
}
void delete(struct node **head, char *namedelete)
{
struct node* temp = *head, *prev;
if (temp != NULL && temp->name == namedelete)
{
*head = temp->next;
free(temp);
return;
}
while (temp != NULL && temp->name != namedelete)
{
prev = temp;
temp = temp->next;
}
if (temp == NULL) return;
prev->next = temp->next;
free(temp);
}
void readfile(struct node *head)
{
FILE *file = fopen("hw8.data", "r");
char tname[42];
char *tempname = (char*)malloc(42*sizeof(char));
char *op_code = (char*)malloc(1*sizeof(char));
while(fgets(tname, sizeof(tname), file))
{
tempname = strtok(tname, " ");
op_code = strtok(NULL, "\n");
printf("%s\n", tempname);
printf("%s\n", op_code);
if (!strcmp(op_code, "a"))
{
addFront(&head, tempname);
}
else if (!strcmp(op_code, "d"))
{
delete(&head, tempname);
}
}
fclose(file);
printList(head);
}
int main()
{
struct node *head = NULL;
readfile(head);
exit(0);
}
The only issue I see with in readFile() is a memory leak.
char *tempname = (char*)malloc(42*sizeof(char));
char *op_code = (char*)malloc(1*sizeof(char));
should just be
char *tempname;
char *op_code;
strtok() breaks up the string in place, it does not produce a copy of the string, so no need to allocate extra memory.
I do see some issues in delete() however
You should be using strcmp() here instead of ==, like you do in readfile()
if (temp != NULL && temp->name == namedelete)
while (temp != NULL && temp->name != namedelete)
You may also need to initialize prev.
With your code as-is, add seemed to be working fine, and with my changes, delete looks ok.
Related
I am unable to correctly specify the end of my list. I am not sure why but I think its because the condition of the while loop is allowing node to take data from memory and then making its next = NULL. I tried to replace node->next = NULL; with node = NULL; but it didn't work. I will be very happy to receive some helpful solutions or tips and thank you in advance.
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
//my variables
typedef struct {
char Fname[len];
char Lname[len];
double salary;
} employee;
// list of employees
typedef struct list {
employee e;
struct list *next;
} list;
void LOAD_LIST(FILE *f, list *head)
{
list *node = (list *)malloc(sizeof(list));
node = head;
while (fscanf(f, "%10s%10s%lf", node->e.Fname, node->e.Lname, &node->e.salary) != EOF)
{
node->next = (list *)malloc(sizeof(list));
if (node->next == NULL)
{
printf("memory allocation failed\n");
break;
}
node = node->next;
}
node->next = NULL;
}
void DISPLAY(list *node)
{
while (node != NULL)
{
printf("%s | %s | %.2lf\n", node->e.Fname, node->e.Lname, node->e.salary);
node = node->next;
}
}
void freeList(struct list *head)
{
list *tmp;
while (head != NULL)
{
tmp = head;
head = head->next;
free(tmp);
}
}
int main()
{
FILE *file = fopen("file.txt", "r");
list *listhead = (list *)malloc(sizeof(listhead));
LOAD_LIST(file, listhead);
DISPLAY(listhead);
freeList(listhead);
fclose(file);
return 0;
}
EDIT: Thanks for the responses I corrected the problem you pointed to but I still don't know how to free the node that got EOF.
Here is the new function:
void LOAD_LIST(FILE *f, list *head)
{
list *node = (list *)malloc(sizeof(list));
fscanf(f, "%10s%10s%lf", head->e.Fname, head->e.Lname, &head->e.salary);
head->next = node;
while (fscanf(f, "%10s%10s%lf", node->e.Fname, node->e.Lname, &node->e.salary) != EOF)
{
node->next = (list *)malloc(sizeof(list));
if (node->next == NULL)
{
printf("memory allocation failed\n");
break;
}
node = node->next;
}
node->next = NULL;
}
I think there are a few problems in your code:
You're always allocating the head of the linked list in main(), what if your file is empty and no data is parsed via file. I think you should modify your LOAD_LIST method to return the new list head instead.
In while loop of your LOAD_LIST method, you're allocating list *node before reading using fscanf. This means you have an extra node allocated no matter what happens during fscanf. I think you should allocate memory after reading via fscanf check whether number of characters parsed is equal to 3.
Not sure why, but you're allocating memory of node->next while you're reading contents of node. What if it's the last line of file you're reading. You'll have an extra node in the end.
I moved your node memory allocation+initialization logic to a method createNewNode and I'm tracking head and prevNode variables to keep track of beginning and last allocated node of your linked list:
list* createNewNode(char firstName[], char lastName[], double salary) {
list* newNode = (list *) malloc(sizeof(list));
if (newNode == NULL) {
printf("Node Memory allocation failed\n");
return NULL;
}
strcpy(newNode->e.fName, firstName);
strcpy(newNode->e.lName, lastName);
newNode->e.salary = salary;
newNode->next = NULL;
}
list* load_list(FILE *f)
{
list *head = NULL, *prevNode = NULL;
char firstName[100], lastName[100];
double salary;
while (fscanf(f, "%10s%10s%lf", firstName, lastName, &salary) == 3) {
list *currentNode = createNewNode(firstName, lastName, salary);
if (currentNode == NULL) {
printf("memory allocation failed\n");
break;
}
if (head == NULL) {
head = currentNode;
prevNode = currentNode;
} else {
prevNode->next = currentNode;
prevNode = currentNode;
}
}
return head;
}
// ...
int main() {
FILE *file = fopen("file.txt", "r");
list* listhead = load_list(file);
display(listhead);
freeList(listhead);
fclose(file);
return 0;
}
I tested with a file like this:
Rohan Kumar 100
Robert Dicosta 200
Rupert Griffin 30
BadInput
This gives the following output:
c-posts : $ ./a.out
Rohan | Kumar | 100.00
Robert | Dicosta | 200.00
Rupert | Griffin | 30.00
I have a problem with this code. I have tried to debug with gdb and Valgrind, But nothing works...
The goal of the code is to create a list, where every string is added only if no existing node with the same string in already part of the list.
This is the code:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct node {
char *word;
struct node *next;
};
void print_list(struct node *head) {
while ((head) != NULL) {
printf(" %s -> ", head->word);
head = (head->next);
}
}
// insert a new node in head
void add_n(struct node **head, char *str) {
struct node *new;
new = malloc(sizeof(struct node));
if (new == NULL) {
printf("Not enough memory\n");
exit(EXIT_FAILURE);
}
new->word = str;
new->next = NULL;
if ((*head) == NULL){
(*head) = new;
}
while ((*head)->next != NULL) {
head = &(*head)->next;
}
(*head)->next = new;
}
// check if str is present in the list
int find_string(struct node **head, char *str) {
int found = 0;
while ((*head) != NULL) {
int i = strcmp(str, (*head)->word); //i=0 are the same
if (i == 0) {
found = 1;
return found;
}
head = &((*head)->next);
}
return found;
}
// insert a new string in the list only if is new
void insert(struct node **head, char *str) {
if (find_string(head, str) == 0) {
add_n(head, str);
}
}
void rem_ent(struct node **head, struct node *ent) {
while ((*head) != ent) {
head = &((*head)->next);
}
(*head) = ent->next;
free(ent);
}
void fini_list(struct node **head) {
while ((*head) != NULL) {
rem_ent(head, *head);
head = &((*head)->next);
}
}
int main() {
struct node *head = NULL;
insert(&head, "electric");
print_list(head);
insert(&head, "calcolatori");
print_list(head);
insert(&head, "prova pratica");
print_list(head);
insert(&head, "calcolatori");
print_list(head);
fini_list(&head);
//printf("lunghezza media = %f\n", avg_word_lenght(head));
return 0;
}
Maybe the error might be stupid, but I spent a lot of time debugging without success.
the function fini_list invokes undefined behavior due to the redundant statement
head=&((*head)->next);
because the function rem_ent already set the new value of the pointer head.
void rem_ent(struct node** head, struct node * ent){
while((*head) != ent){
head= &((*head)->next);
}
(*head)= ent->next;
free(ent);
}
Remove the statement
void fini_list(struct node** head){
while((*head) != NULL){
rem_ent(head, *head);
}
}
Also change the function add_n the following way
// insert a new node in head
void add_n(struct node ** head, char* str){
struct node * new;
new = malloc(sizeof(struct node));
if (new == NULL) {
printf("Not enough memory\n");
exit(EXIT_FAILURE);
}
new->word= str;
new->next = NULL;
if ((*head)==NULL){
(*head)=new;
}
else
{
while((*head)->next != NULL){
head = &(*head)->next;}
(*head)->next = new;
}
}
And next time format the code such a way that it would be readable.
In general you should allocate dynamically memory for strings that will be stored in nodes of the list.
After reviewing the information regarding the linked list (still trying to make my head around the topic). I rewrote the following code, and this is the final version: (the code aims to prompt for a directory, and a wildcard, to create a linked list of files found in that directory with this file extension.
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct nlist{
char *data;
struct nlist *next;
}Node;
Node* insert(Node*, char*);
void show(Node*);
Node* insert(Node *Head, char *value)
{
Node *new_string;
new_string = (Node *)malloc(sizeof(Node));
new_string->data = malloc(strlen(value)+1);
strcpy(new_string->data,value);
Node *check;
check = (Node *)malloc(sizeof(Node));
if(Head == NULL){
Head = new_string;
Head->next = NULL;
}
else{
check = Head;
while(check->next != NULL)
check = check->next;
check->next = new_string;
new_string->next = NULL;
}
return Head;
}
void show(Node *Head)
{
Node *check;
check = (Node *)malloc(sizeof(Node));
check = Head;
if (check == NULL){
return;
}
while(check != NULL) {
printf("%s", check->data);
check=check->next;
}
printf("\n");
}
void listFilesRecursively(char *path, char *suffix);
int main()
{
char path[100];
char suffix[100];
// Input path from user
// Suffix Band Sentinel-2 of Type B02_10m.tif
printf("Enter path to list files: ");
scanf("%s", path);
printf("Enter the wildcard: ");
scanf("%s", suffix);
listFilesRecursively(path, suffix);
return 0;
}
int string_ends_with(const char * str, const char * suffix)
{
int str_len = strlen(str);
int suffix_len = strlen(suffix);
return
(str_len >= suffix_len) &&
(0 == strcmp(str + (str_len-suffix_len), suffix));
}
void listFilesRecursively(char *basePath, char *suffix)
{
char path[1000];
struct dirent *dp;
DIR *dir = opendir(basePath);
Node *Head = NULL;
if (!dir)
return;
while ((dp = readdir(dir)) != NULL)
{
if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0)
{
strcpy(path, basePath);
strcat(path, "/");
strcat(path, dp->d_name);
if (string_ends_with(path, suffix))
Head = insert(Head, path);
listFilesRecursively(path, suffix);
}
}
//show(Head);
/*
Node *check;
check = (Node *)malloc(sizeof(Node));
check = Head;
if (check == NULL){
return;
}
while(check != NULL) {
printf("%s", check->data);
//open_array(check->data);
check=check->next;
}
printf("\n");
//return Head;
*/
Node *p;
p = (Node *)malloc(sizeof(Node));
for (p = &Head; p != NULL; p = p->next){
printf(stdout, "Data: %s\n", p->data);
}
closedir(dir);
}
My problem:
I commented the while loop in which I am able to print out the Nodes Data that I chained when
using the insert(Head, path) function. However, When I am using a for loop to do the same
Node *p // to create a Node pointer
p = (Node*)malloc(sizeof(Node)); // to allocate space for that node
for (p = &Head; p!= NULL; p = p->next){
printf(stdout, "Data: %s\n", p->data); // to print the nodes in the for loop starting by getting the address of the Head of the linked list
}
Why do I incur in a segmentation fault? Is the iteration over a linked list possible using a for loop, the same as using a while loop in C?
Node *p;
if (!(p = (Node*)malloc(sizeof(Node))))
return;
for (p = Head; p != NULL; p = p->next){
printf(stdout, "Data: %s\n", p->data);
}
No need to reference to your Head's address as it is already one.
Protecting your programs against malloc fails is also a good habit.
I am currently writing a program that implements a linked list to maintain a list on integers in sorted order. The program reads the integers from a file in the format of
d [\t] 7
i [\t] 8
i [\t] 7
i [\t] 10
with 'd' meaning delete from the list and 'i' inserting into the list, with no repeat integers allowed. This is the code I have so far:
#include <stdio.h>
#include <stdlib.h>
struct Node{
int data;
struct Node* next;
};
int delete(struct Node** head, int key){
if(head == NULL)
{
return -1;
}
struct Node* ptr = *head;
struct Node* prev = NULL;
while(ptr->data != key && ptr->next!=NULL)
{
prev = ptr;
ptr = ptr->next;
}
if(ptr->data == key)
{
if(prev)
{
prev->next = ptr->next;
}
else
{
*head = ptr->next;
}
free(ptr);
return key;
}
return -1;
}
int insert(struct Node** head, struct Node* newNode){
struct Node* ptr;
if(*head == NULL || (*head)->data >= newNode->data)
{
newNode->next = *head;
*head = newNode;
}
else
{
ptr = *head;
while(ptr->next != NULL && ptr->next->data < newNode->data)
{
ptr = ptr->next;
}
newNode->next = ptr->next;
ptr->next = newNode;
}
return 0;
}
int main(int argc, char* argv[]){
FILE *fp = fopen(argv[1], "r");
int num, found;
char c;
struct Node* head = NULL;
struct Node* ptr = head;
while(fscanf(fp, "%c\t%d\n", &c, &num)!=EOF)
{
for(ptr=head; ptr!=NULL; ptr=ptr->next)
{
if(ptr->data == num)
{
found = 1;
}
}
if(found != 1)
{
if(c == 'i')
{
struct Node *newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = num;
newNode->next = NULL;
insert(&head, newNode);
}
}
if(c == 'd')
delete(&head, num);
}
found = 0;
}
for(ptr=head; ptr!=NULL; ptr=ptr->next)
{
printf("%d ", ptr->data);
}
return 0;
}
It works fine when the first letter is 'i', but whenever the first letter is 'd' I get a segmentation fault. Why is this?
When you try to delete a node first thing you do, the list is empty and inside the delete function *head (notice the dereference!) is a null pointer.
Since *head is a null pointer, so will ptr be, and you then dereference ptr without checking for that.
You might want to modify the initial check to be head == NULL || *head == NULL.
whenever the first letter is 'd' I get a segmentation fault.
If the first letter is D, your list is still empty when you call delete.
In your first if, you need to dereference your pointer, as #SomeProgrammerDude mentionned.
I implemented a linked list in c. when i test it, whenever i reach display function or insert at the end the program crashes. These are the functions:
struct Node
{
char data;
struct Node *next;
};
struct LinkedList
{
struct Node *head;
};
void insertAtBeginning(struct LinkedList *LL, char ele)
{
struct Node *new = (struct Node*)malloc(sizeof(struct Node));
new->data = ele;
new->next = NULL;
if(new->data == '\n')return;
if(LL->head==NULL)LL->head = new;
else
{
new->next=LL->head;
LL->head=new;
}
}
void insertAtTheEnd(struct LinkedList *LL, char ele)
{
struct Node *new = (struct Node*) malloc(sizeof(struct Node));
new->data = ele;
new->next = NULL;
if(LL->head==NULL){LL->head=new;return;}
struct Node *current = LL->head;
while(current->next != NULL) {current = current->next;}
current->next = new;
}
void deleteNode(struct LinkedList* LL, char ele)
{
struct Node *current = LL->head;
struct Node *temp = LL->head;
while(current->next->data!=ele && current!=NULL)current=current->next;
temp=current->next;
current->next=current->next->next;
free(temp);
}
void deleteFirstNode(struct LinkedList* LL)
{
struct Node *temp;
if(LL->head != NULL)
{
temp = LL->head;
LL->head = LL->head->next;
free(temp);
}
}
void displayLinkedList(struct LinkedList LL)
{
struct Node *current = LL.head;
printf("List: ");
while(current != NULL)
{
printf("%c",current->data);
current = current->next;
}
printf("\n");
}
This is the main:
main()
{
struct LinkedList LL;
char c = '0';
//Inserting at beginning
printf("Type a string. Press Enter to end: ");
while(c != '\n')
{
scanf("%c",&c);
insertAtBeginning(&LL, c);
}
printf("List: ");
displayLinkedList(LL);
printf("\n");
//Inserting at end
c='0';
printf("Type a string. Press Enter to end: ");
while(c != '\n')
{
scanf("%c",&c);
insertAtTheEnd(&LL, c);
}
printf("List: ");
displayLinkedList(LL);
printf("\n");
//Remove
printf("Enter a char to remove: ");
scanf("%c",&c);
deleteNode(&LL, c);
printf("\n");
printf("List: ");
displayLinkedList(LL);
printf("\n");
deleteFirstNode(&LL);
printf("List: ");
displayLinkedList(LL);
}
of course This is done after inserting the necessary libraries.
you have to set NULL to next when you create element. if you do not do it, your while loop behaviour will be buggy
void insertAtTheEnd(struct LinkedList *LL, char ele)
{
struct Node *new = (struct Node*) malloc(sizeof(struct Node));
new->next = NULL;
new->data = ele;
if(LL->head == NULL) LL->head = new;
else
{
struct Node *current = LL->head;
while(current->next != NULL) {current = current->next;}
current->next = new;
}
}
, and do same for other functions.
also you have to set NULL to your LinkedList.head. so, set NULL when you define LinkedList
struct LinkedList LL;
LL.head = NULL;
i asume the line if(current = LL->head) is wrong
bacause temp = current->next would crash if the result is NULL
otherwise the code after the if statement doesnt makes sense
There are some instances where you will try to insert at the end. When you have something of the form node_new->next->prev = node, you must check to make sure that node_new->next is not NULL, because NULL does not have any type and thus does not have a prev field.
You may also consider making all of your functions return int, so that you can know if and what goes wrong.