I am using the below code to add nodes to a double linked list and arrange them such that they are in alphabetical order according to their word. The nodes consist of char * word, struct NODES * prev, and struct NODES * next. I am running into an issue when, after reading file testing a, the list looks like NULL <- a <-> file <-> testing -> NULL that adding a node containing word = c it places c before a instead of between a and file. The function prints "Add: Placing c before list head c" and seems to be evaluating c < a. But even with that evaluation being incorrect I do not know how it is replacing a before doing any node manipulation at all. If anyone know what could cause this issue I'd appreciate the advice. P.S. incoming NODES * arg is always in the form of arg -> next == NULL; arg -> prev == NULL; arg -> word != NULL; but list can have all fields NULL if no nodes have been added yet, list -> prev should always be NULL at the time the function is called and when the function terminates.
int addToList(struct NODES * list, struct NODES * arg){
fprintf(stderr,"Add: Adding %s\n", arg->word);
if(list->word == NULL){
list->word = (char *)malloc(strlen(arg->word));
strcpy(list->word, arg->word);
list->next = NULL;
list->prev = NULL;
fprintf(stderr,"Add: Head %s\n", list->word);
return 2;
}
struct NODES * abc = list;
while(abc->word != NULL){
if(strcmp(abc->word, arg->word)<0){
fprintf(stderr, "abc %s < arg %s", abc->word, arg->word);
if (abc->next != NULL)
abc = abc->next;
else{
abc->next = malloc(sizeof(NODE));
abc->next->prev = abc;
abc = abc->next;
abc->next = NULL;
abc->word = NULL;
}
}
else if(abc == list){
fprintf(stderr, "Add: Placing %s before list head %s\n", arg->word, list->word);
arg->next = list;
list->prev = arg;
arg->prev = NULL;
list = arg;
fprintf(stderr, "Add: Placed %s before %s\n", list->word, list->next->word);
return 3;
}
else{
fprintf(stderr, "Add: Placing %s between %s and %s\n", arg->word, abc->word, abc->next->word);
arg->next = abc;
arg->prev = abc->prev;
if(abc->prev != NULL)
abc->prev->next = arg;
abc->prev = arg;
fprintf(stderr, "Added %s after %s and before %s\n", arg->word, arg->prev, arg->next->word);
return 1;
}
}
abc->word = (char *)malloc(strlen(arg->word));
strcpy(abc->word, arg->word);
fprintf(stderr, "Added %s after %s and before %s\n", abc->word, abc->prev->word, abc->next);
return 1;
}
Updated to reflect suggestions:
int addToList(struct NODES ** list, struct NODES * arg){
fprintf(stderr,"Add: Adding %s current head %s\n", arg -> word, (*list)->word);
if((*list) -> word == NULL){
(*list) -> word = malloc(strlen(arg->word)+1);
strcpy((*list) -> word, arg -> word);
(*list) -> next = NULL;
(*list) -> prev = NULL;
fprintf(stderr,"Add: Head %s\n", (*list) -> word);
return 2;
}
struct NODES * abc = (*list);
//while arg > abc
fprintf(stderr,"Comparing %s and %s\n", abc->word,arg->word);
while(strcmp(abc->word, arg->word)<0){
fprintf(stderr,"Comparing %s and %s\n", abc->word,arg->word);
if (abc -> next == NULL)
break;
abc = abc -> next;
}
if (abc == (*list)){
if(!(strcmp(abc->word, arg->word)<0)){
arg -> next = abc;
arg -> prev = NULL;
abc -> prev = arg;
*list = arg;
}
else{
abc -> next = arg;
arg -> prev = abc;
abc -> next = NULL;
}
return 5;
}
if(abc -> next != NULL){
fprintf(stderr, "Inserting %s between %s and %s\n", arg -> word, abc->prev->word,abc->word);
arg -> next = abc;
arg -> prev = abc -> prev;
arg -> prev -> next = arg;
abc -> prev = arg;
fprintf(stderr, "Added %s before %s and after %s\n", arg->word, arg->prev->word,arg->next->word);
return 3;
}
return 0
}
The list argument received by the function is a copy of the list pointer the caller has. To return a revised list pointer the function could be like this:
int addToList(struct NODES ** list, struct NODES * arg)
and it would be called something like this:
result = addToList(&list, arg);
The function would provide a new list pointer like this
*list = arg;
and all the list access you currently have would be one step more indirect
if(list->word == NULL)
would become
if((*list)->word == NULL)
UPDATE
Try this simplified code, I found this easier than getting my head round yours.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct NODES {
struct NODES *prev;
struct NODES *next;
char *word;
};
void showList(struct NODES * list) {
while (list) {
if (list->prev)
printf("%-10s", list->prev->word);
else
printf("%-10s", "NULL");
printf(" <- %-10s -> ", list->word);
if (list->next)
printf("%-10s", list->next->word);
else
printf("%-10s", "NULL");
printf("\n");
list = list->next;
}
}
int addToList(struct NODES ** list, char *word){
struct NODES *wptr, *lptr = *list, *pptr = NULL;
if ((wptr = malloc(sizeof(struct NODES))) == NULL)
return -1;
wptr->prev = NULL;
wptr->next = NULL;
if ((wptr->word = strdup(word)) == NULL)
return -2;
if (lptr == NULL) {
*list = wptr; // first list item
return 0;
}
while (lptr) {
if (strcmp(word, lptr->word) <= 0) {
wptr->next = lptr; // insert before current node
wptr->prev = pptr;
if (pptr)
pptr->next = wptr;
else
*list = wptr;
lptr->prev = wptr;
return 1;
}
pptr = lptr;
lptr = lptr->next;
}
wptr->prev = pptr; // insert at tail
pptr->next = wptr;
return 2;
}
int main()
{
struct NODES *list = NULL;
addToList(&list, "one");
addToList(&list, "two");
addToList(&list, "three");
addToList(&list, "four");
addToList(&list, "five");
showList(list);
return 0;
}
Program output:
NULL <- five -> four
five <- four -> one
four <- one -> three
one <- three -> two
three <- two -> NULL
I suspect your problem is here:
list->word = (char *)malloc(strlen(arg->word));
strcpy(list->word, arg->word);
Since you do not allocate room for the terminating null-character, later calls to strcmp will read beyond the allocated buffer. This can have all sorts of behaviours, most likely also the one you see.
Also, drop the (char *) cast, it may hide other problems with your code.
Related
i'm trying to take input from a text file and put the value in a linked list variables,
my file(text.txt) in in the following format:
the first value is my burst time, the second arrival and the last value is the priority, i want to make operations on all of these value from the same line like burst time + prioriy and so on, and when the operation for the first line ends up, the program should pass to the next line until the time the prom will fin the EOF.
now the big issue is when i'm trying to read each character and store each in a variable of a linked list for manipulation. find down my code:
#include <stdio.h>
#include <stdlib.h>
typedef struct Node{
int x;
struct Node* next;
}Node;
void insert_end(Node** root, int value)
{
Node* new_node = malloc(sizeof(Node));
if(new_node == NULL)
{
exit(1);
}
new_node->next = NULL;
new_node->x = value;
if(*root == NULL)
{
*root = new_node;
return;
}
Node* curr = *root;
while(curr->next != NULL)
{
curr = curr->next;
}
curr->next = new_node;
}
//the part i'm reading the file
void deserialize(Node** root)
{
FILE* file = fopen("text.txt","r");
if(file == NULL)
{
exit(2);
}
int val;
int val2;
while(fscanf(file, "%d", &val)>0)
{
insert_end(root, val);
}
fclose(file);
}
int main(int argc, char* argv[])
{
Node* root = NULL;
if(root == NULL)
{
exit(2);
}
root->x = 15;
root->next = NULL;
deserialize(&root);
for(Node* curr = root; curr != NULL; curr = curr->next)
{
printf("%d\n",curr->x);
}
deallocate(&root);
return 0;
}
i really need your help, thank you
A complete example including serialize() deserialize() and linked list functions are here after the "Now What?" paragraph
May be you could write this in 2 steps. First try to consume the data on file, then put the code to insert this into a linked list..
About the struct
typedef struct Node
{
int x;
struct Node* next;
} Node;
maybe not the best way to describe a linked list. This is just a node and not a list. You will be better served with a bit of encapsulation. A list has metadata and pointers to nodes. Nodes points to or contain data. A list is NOT a node. A node is NOT a list. Program a linked list with just a node is problematic at best. Many loose pointers and your case even more problematic Node** pointers.
Compare with something like
typedef struct
{
unsigned burst;
unsigned arrival;
unsigned priority;
} Info;
typedef struct st_node
{
Info* info;
struct st_node* next;
struct st_node* prev;
} Node;
typedef struct
{
Node* head;
Node* tail;
unsigned size;
} List;
List* create();
List* destroy(List* list);
int insert(Info* one_item, List* list);
And see that a list is a list of nodes. Nodes points to info and info is the unit of data. Any data.
insert() inserts an item into a list, create() and destroy() manages lists and the encapsulation makes everything far more easier than just using pointers and pointers to pointers.
Consuming the file
Using this file as input.txt
2:101:34
20:10:3
5:1:4
and this data unit
typedef struct
{
unsigned burst;
unsigned arrival;
unsigned priority;
} Info;
See that the input is a CSV --- from Comma Separated Values --- file, a format from '70s. The separator is a ':'.
scanf() and family was written for this: Scan Formatted Files, hence the name. It is a scanner. So it is easier to just use it in this way.
See this example
List* deserialize(const char* file)
{
FILE* in = fopen(file, "r");
if (in == NULL) return NULL;
Info info = {0};
List* new_l = create(); // new list here
while (3 == fscanf(
in, "%d:%d:%d", &info.arrival,
&info.burst, &info.priority))
{
fprintf(
stderr, "%d:%d:%d\n", info.arrival, info.burst,
info.priority);
insert(&info, new_l); // insert data into list
};
fclose(in);
return new_l; // returns a list with the data on file
}
and the output
2:101:34
20:10:3
5:1:4
when called as
List* new_list = deserialize("input.txt");
as expected. And a List is returned with the data on file...
The complete code
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
unsigned burst;
unsigned arrival;
unsigned priority;
} Info;
typedef struct st_node
{
Info* info;
struct st_node* next;
struct st_node* prev;
} Node;
typedef struct
{
Node* head;
Node* tail;
unsigned size;
} List;
int insert(Info* one_item, List* list);
List* create();
List* destroy(List*);
List* deserialize(const char* file)
{
FILE* in = fopen(file, "r");
if (in == NULL) return NULL;
Info info = {};
List* new_l = create(); // new list here
while (3 == fscanf(
in, "%d:%d:%d", &info.arrival,
&info.burst, &info.priority))
{
fprintf(
stderr, "%d:%d:%d\n", info.arrival, info.burst,
info.priority);
insert(&info, new_l); // insert data into list
};
fclose(in);
return new_l; // returns a list with the data on file
}
int main(void)
{
List* new_list = deserialize("input.txt");
new_list = destroy(new_list);
return 0;
}
int insert(Info* one_item, List* list) { return 0; }
List* create()
{
List* L = (List*)malloc(sizeof(List));
if (L == NULL) return NULL;
L->size = 0;
L->head = NULL;
L->tail = NULL;
return L;
}
List* destroy(List* list) { return NULL; }
Now what?
Now we have
List* deserialize(const char* file)
that can consume the data from a file like this
input.txt:
3:2:1
6:4:2
12:8:3
24:16:4
by using
List* new_list = deserialize("input.txt");
But deserialize() just prints the data on stderr
We want a linked list of the dataset.
I will write an example below, but step by step in order to cover other cases for other readers.
A linked list
I will add a sequence number to the nodes just to help in testing.
The linked list has nothing to do with our problem, or else we will need one implementation of linked list for every program in life. In fact would be great if the code for the list was in another source code file, in order to be used anywhere else.
this is the example for List
typedef struct st_node
{
int num;
Info* info;
struct st_node* next;
struct st_node* prev;
} Node;
typedef struct
{
size_t size;
Node* head;
Node* tail;
} List;
A list of nodes, nodes pointing to info.
info is from now on
// info is the thing in the list
typedef struct
{
unsigned burst;
unsigned arrival;
unsigned priority;
unsigned seq; // sequence number for testing
} Info;
// this is a helper to format a single node listing
int show_i(Info*, const char*);
The code for show_i() is simple
int show_i(Info* info, const char* msg)
{
if (info == NULL) return -1;
if (msg != NULL) printf("%s", msg);
printf(
"#%4d: B:%4d A:%4d P:%4d\n", info->seq, info->burst,
info->arrival, info->priority);
return 0;
}
And the reason for this to exist is to provided some encapsulation on the way the nodes are printed.
And the functions we will use here are the obvious ones:
List* create_l();
List* destroy_l(List*);
int empty(List*);
int insert_n(Info*, List*);
int remove_n(List*);
int show_l(List*, const char*);
int size(List*);
This is a very simple example so we will assume a standard queue, FIFO (First In First Out) one, with items added at the end and removed from the front.
empty() returns 1 if the list is empty
size() returns the expected size
show_l() shows the list contents with an optional title message
the other functions work as expected.
A simple C implementation of the linked list
Functions have no more than 10 to 15 lines
List* destroy_l(List* L)
{
if (L == NULL) return NULL;
Node* p = L->head;
for (size_t i = 0; i < L->size; i += 1)
{ // remove one by one
Node* nx = p->next;
free(p->info); // free data
free(p); // free node
p = nx;
}; // for
free(L); // free list
return NULL; // to invalidate pointer
}
int empty(List* L)
{
if (L == NULL) return 0;
return (L->size == 0);
}
List* create_l()
{
List* nv = (List*)malloc(sizeof(List));
if (nv == NULL) return NULL;
nv->size = 0; // vazia
nv->head = NULL;
nv->tail = NULL;
return nv;
}
int insert_n(Info* info, List* L)
{ // inserts at the end of list
static unsigned seq = 1000;
if (L == NULL) return -1;
// new node here
Node* nv = (Node*)malloc(sizeof(Node));
// new data here: always copy
nv->info = (Info*)malloc(sizeof(Info));
*(nv->info) = *info;
nv->info->seq = seq++; // USN
nv->prev = L->tail;
nv->next = NULL;
// ajusta os ponteiros da lista
L->size += 1; // conta o novo
if (L->size == 1)
L->head = nv;
else { L->tail->next = nv; }
L->tail = nv;
return (int)L->size;
}
int remove_n(List* L)
{ // remove from start
if (L == NULL) return -1;
if (L->size == 0) return -2;
Node* p = L->head->next;
free(L->head->info); // data
free(L->head); // node
L->head = p;
L->size -= 1;
if (L->size == 0) L->tail = NULL;
return (int)L->size;
}
int show_l(List* L, const char* tit)
{
if (L == NULL) return -1;
if (tit != NULL) printf("%s", tit);
if (L->size == 0)
printf(" no elements\n");
else
printf(" %zd elements:\n", L->size);
if (L->head != NULL)
printf(" [First seq: %d", L->head->info->seq);
if (L->tail != NULL)
printf(" Last seq: %d]\n", L->tail->info->seq);
Node* p = L->head;
for (size_t i = 0; i < L->size; i += 1)
{
show_i(p->info, "\t");
p = p->next;
}
printf("\n");
return 0;
}
int size(List* L)
{
if (L == NULL) return 0;
return (int)L->size;
}
A test program for the list code
#include <stdio.h>
#include "v2-l.h"
int main(void)
{
Info info = {1, 1, 1, 1};
List* my_list = create_l();
show_l(my_list, "empty list...\n");
my_list = destroy_l(my_list);
my_list = create_l();
// test size
const int test_size = 3;
printf("[Testing with %d elements]\n\n\n", test_size);
for (int i = 0; i < test_size; i += 1)
{
info.priority = i; // just for testing
insert_n(&info, my_list);
};
char message[] = "NNNNNN elements inserted\n";
sprintf(message, "%d elements inserted\n", test_size);
show_l(my_list, message);
int res = 0;
while (res >= 0)
{
printf("\tabout to remove 1st element:\n");
res = remove_n(my_list);
printf(
"\
\tremove_l() returned %d\n\
\tsize() returned %d\n\
\tempty() returned %d\n",
res, size(my_list), empty(my_list));
show_l(my_list, "\n ==> List now:\n");
if (res < 0) break;
}; // while()
show_l(my_list, "On exit\n");
my_list = destroy_l(my_list);
return 0;
}
The idea: creates a list with test_size elements and then remove one by one until error, calling the functions. Then the list is destroyed.
output from test
empty list...
no elements
[Testing with 3 elements]
3 elements inserted
3 elements:
[First seq: 1000 Last seq: 1002]
#1000: B: 1 A: 1 P: 0
#1001: B: 1 A: 1 P: 1
#1002: B: 1 A: 1 P: 2
about to remove 1st element:
remove_l() returned 2
size() returned 2
empty() returned 0
==> List now:
2 elements:
[First seq: 1001 Last seq: 1002]
#1001: B: 1 A: 1 P: 1
#1002: B: 1 A: 1 P: 2
about to remove 1st element:
remove_l() returned 1
size() returned 1
empty() returned 0
==> List now:
1 elements:
[First seq: 1002 Last seq: 1002]
#1002: B: 1 A: 1 P: 2
about to remove 1st element:
remove_l() returned 0
size() returned 0
empty() returned 1
==> List now:
no elements
about to remove 1st element:
remove_l() returned -2
size() returned 0
empty() returned 1
==> List now:
no elements
On exit
no elements
Using the list on the original program
// here goes the program target
List* deserialize(const char* file);
int serialize(List* list, const char* file);
double pri_avg(List*);
The next obvious step is to use the 1st program and consume the file, but this time writing the data into a linked list, and then calling serialize() to create a new file with the data in the list.
Sure, both files must have the same data and the program will test itself.
As you asked, #jonathan pascal, the function pri_avg() computes an useless priority average, just to show how to compute something using the data from all nodes in the list.
Note that this is the same logic as in showing the list contents in show_l(). This is called a filter and in languages like C we can just pass a function address to a function that loops over the code, and use the same code to do anything with the dataset. In C++ is, for example, a for_each() function that does just this. All these functions here has the same logic.
Example implementation of the 3 functions
List* deserialize(const char* file)
{
FILE* in = fopen(file, "r");
if (in == NULL) return NULL;
Info info = {0};
List* new_l = create_l(); // new list here
while (3 == fscanf(
in, "%d:%d:%d", &info.burst,
&info.arrival, &info.priority))
{
// fprintf(
// stderr, "%d:%d:%d\n", info.arrival,
// info.burst, info.priority);
insert_n(&info, new_l); // insert data into list
};
fclose(in);
return new_l; // returns a list with the data on file
};
int serialize(List* L, const char* file)
{
if (L == NULL) return -1;
if (file == NULL)
{
printf("Missing file name\n");
return -2;
}
if (L->size == 0)
{
printf("Dataset is empty\n");
return -3;
}
FILE* out = fopen(file, "w");
if (out == NULL) return -3;
fprintf(
stderr,
"serialize(): writing %d elements into \"%s\"\n",
size(L), file);
Node* p = L->head;
for (size_t i = 0; i < L->size; i += 1)
{
fprintf(
out, "%d:%d:%d\n", p->info->burst,
p->info->arrival, p->info->priority);
p = p->next;
}
fprintf(out, "\n");
fclose(out);
fprintf(stderr, "\"%s\" closed\n", file);
return 0;
}
// get the priority average from the list
double pri_avg(List* L)
{
if (L == NULL) return -1;
if (L->size == 0) return 0.; // easy
double avg = 0.;
Node* p = L->head;
for (size_t i = 0; i < L->size; i += 1)
{ // here we have node data, one
// at a time
avg = avg + p->info->priority;
p = p->next;
};
return (double)avg / size(L);
}
Testing the 3 functions
#include <stdio.h>
#include "v2-l.h"
int main(void)
{
const char* in_file = "input.txt";
printf(
"deserialize(): building list from \"%s\"\n",
in_file);
List* my_list = deserialize(in_file);
show_l(my_list, " ==> As read from file...\n");
printf("average priority is %6.2f\n", pri_avg(my_list));
const char* out_file = "another.txt";
int res = serialize(my_list, out_file);
printf(
"serialize(): dumping list into \"%s\" "
"returned %d\n",
out_file, res);
my_list = destroy_l(my_list);
return 0;
}
And we have full circle over the problem: a CSV file is read from disk, a linked list is built, some values are computed, the list is written to disk in another file.
test output
deserialize(): building list from "input.txt"
==> As read from file...
4 elements:
[First seq: 1000 Last seq: 1003]
#1000: B: 3 A: 2 P: 1
#1001: B: 6 A: 4 P: 2
#1002: B: 12 A: 8 P: 3
#1003: B: 24 A: 16 P: 4
average priority is 2.50
serialize(): writing 4 elements into "another.txt"
"another.txt" closed
serialize(): dumping list into "another.txt" returned 0
The format string %d does not match the input :101 ,since : cannot be part of an integer. So scanf("%d") consumes nothing and the : is left in the input stream. You could use: while(fscanf(file, "%4d:", &val) == 1). If you are reading the last value on a line, the : will not match, but in this case you don't care. You might want to use fscanf(file, "%4d:%4d:%4d", ...) == 3 if you want to check the format of the input (ie, always exactly 3 inputs per line, so you can reject lines like 1:2:3:4:5:6:7). YMMV
I'm attempting to extract text from a text file using linked lists. My code so far:
#include <stdlib.h>
#include <string.h>
#define N 20
struct node
{
char * str;
struct node * node;
};
typedef struct node Node;
typedef Node * WordList;
typedef Node * Node_ptr;
void addtext(WordList * lst_ptr);
void report(WordList words);
Node * createnode(char *str);
int main(void)
{
WordList words = NULL;
addtext(&words);
report(words);
return 0;
}
Node * createnode(char *str)
{
Node_ptr newnode_ptr ;
newnode_ptr = malloc(sizeof (Node));
newnode_ptr -> str = NULL;
newnode_ptr -> node = NULL;
return newnode_ptr;
}
void addtext(WordList * lst_ptr)
{
FILE *fileinput;
char word[N];
char *pnt;
if ((fileinput = fopen("test.txt", "rt"))==NULL)
{
printf("Cannot open file. Please check if the file is in the right directory and you are giving the correct name.");
exit(EXIT_FAILURE);
}
while (fscanf(fileinput, "%19s", word) == 1)
{
Node * newnode = createnode(word);
if (*lst_ptr==NULL)
{
newnode->node = *lst_ptr;
*lst_ptr = newnode;
}
else
{
Node *iter;
for (iter = *lst_ptr; iter -> node != NULL; iter = iter -> node);
iter -> node = newnode;
}
pnt = strtok (word," ");
while (pnt != NULL)
{
newnode->str = pnt;
pnt = strtok (NULL, " ");
}
}
fclose(fileinput);
return ;
}
void report(WordList words)
{
if (words == NULL)
{
printf("\n") ;
return ;
}
printf("%s\n",words -> str);
report(words -> node);
return;
}
The text file I've made contains the following:
Hello
Bye
But the output of my program is:
Bye
(random characters)
Meaning that it prints the last word. My guess is that when I call report it point to the last element of the list and keeps going from there for as many times as the amount of words in the text file.
I also tried a different approach on the report function:
void report(WordList words)
{
Node * iter;
for (iter = words; iter!=NULL; iter = iter->node)
{
printf("%s\n", iter->str);
}
return;
}
But now it prints \n until the loop ends. Am I missing something simple or am I making a grave mistake?
The strtok() modifies the original string and returns pointers to elements of the original string. In this case the original string is stored in a local array char word[N];. The local array will vanish on returning the function addtext and refering the array from the function report after that is illegal.
To avoid this trouble, you should allocate some new regions and copy the string there. It can be done like this:
pnt = strtok (word," ");
while (pnt != NULL)
{
newnode->str = malloc(strlen(pnt) + 1); /* allocate a region (+1 for terminating null-character) */
if (newnode->str == NULL) /* check if allocation succeeded */
{
/* handle allocation failure */
exit(1);
}
strcpy(newnode->str, pnt); /* copy the string */
pnt = strtok (NULL, " ");
}
Simplified:
newnode->str = strdup(pnt);
I am writting a program on doubly linked list with the following data structure:
typedef struct telephoneBookNode {
int id;
char name[NAME_LENGTH];
char telephone[TELEPHONE_LENGTH];
struct telephoneBookNode * previousNode;
struct telephoneBookNode * nextNode;
} TelephoneBookNode;
typedef struct telephoneBookList {
TelephoneBookNode * head;
TelephoneBookNode * tail;
TelephoneBookNode * current;
} TelephoneBookList;
In the following function, I read data from a text file to the linked list, the file content look like this:
/*100, Alice, 0411112222
101, Bob, 0411112222
102, Ali, 0411112223*/
TelephoneBookList * commandLoad(char* fileName) {
TelephoneBookList *(*createList)(TelephoneBookNode*, char[]) = createTelephoneBookList;
char entry[100], *temp1, *temp2;
TelephoneBookList* aList = NULL;
TelephoneBookNode* aNode = NULL;
FILE* telephoneListFile = NULL;
int countEntry = 0;
Boolean check;
telephoneListFile = fopen(fileName, "r");
if (!telephoneListFile)
return NULL;
else {
while (fgets(entry, 100, telephoneListFile)) {
temp2 = strcpy(temp2, entry);
temp1 = strtok(entry, "\n");
check = addressBookEntryCheck(temp1);
if (!check)
return NULL;
else
//here I pass aNode pointer to the below function
aList = (*createList)(aNode, temp2);
}
fclose(telephoneListFile);
printf("printed"); //This line is reached when program complied
return aList;
}
}
This is the function to create the list, problem may be here: it doesnot add new node to the list, it just replaces the first node with the new one. Finally, the linked list only has 1 record which was the last one in text file. How can I fix the code? Thank you!
TelephoneBookList * createTelephoneBookList(TelephoneBookNode* node, char entry[]) {
TelephoneBookList* aList = malloc(sizeof *aList);
TelephoneBookNode* aNode = (TelephoneBookNode*) malloc(sizeof *aNode);
char *tokens;
tokens = strtok(entry, ", ");
aNode->id = atoi(tokens);
tokens = strtok(NULL, ", ");
strcpy(aNode->name, tokens);
tokens = strtok(NULL, ", ");
strcpy(aNode->telephone, tokens); //Just assigning values to a node
//program always go to this block, means `node` is always null
if (node == NULL) {
aNode->nextNode = NULL;
aNode->previousNode = NULL;
node = aNode;
aList->current = node;
aList->head = node;
aList->tail = node;
}
else { //This block is not reached
while (node->nextNode)
node = node->nextNode;
node->nextNode = aNode;
aNode->previousNode = node;
aList->tail = node->nextNode;
}
return aList;
}
This is the function to check entry:
Boolean addressBookEntryCheck(char entry[]) {
char *tokens;
tokens = strtok(entry, ", ");
if(!tokens || strlen(tokens) < 1 || strlen(tokens) > 3)
return FALSE;
else {
if (!isNumber(tokens))
return FALSE;
else {
tokens = strtok(NULL, ", ");
if (!tokens)
return FALSE;
else
{
tokens = strtok(NULL, ", ");
if (!tokens)
return FALSE;
else if (!isNumber(tokens) || strlen(tokens) != 10)
return FALSE;
else
return TRUE;
}
}
}
}
Every time you call
createTelephoneBookList
you create a new list
TelephoneBookList* aList = malloc(sizeof *aList);
You also copy to an uninitialized pointer
temp2 = strcpy(temp2, entry);
I would suggest you create one function to create the list header, one function to add new items e.g.
aList = createList()
while (fgets(entry,sizeof(entry),fp)!=NULL)
{
if (!addEntry(aList,entry))
{
fprintf(stderr, "failed additem item %s\n", entry);
}
}
...
In addEntry parse the string
int id = 0;
char name[NAME_LENGTH];
char telephone[TELEPHONE_LENGTH];
p = strtok(entry, ","); // id
if (p != NULL)
{
id = atoi(p);
p = strtok(NULL, ","); // name, store to temporary string
if (p != NULL )
{
strcpy(name,p);
p = strtok(NULL, ","); // telephone number, store to temporary string
if ( p != NULL )
{
strcpy(telephone,p);
// here you can allocate the new node
}
}
}
// disclaimer omitted checks for length etc which any good program should have. also make sure you have room for \0
if any of the strtok above fail return 0 otherwise allocate a new entry
TelephoneBookNode* aNode = malloc(sizeof(TelephoneBookNode));
aNode->id = id;
strcpy(aNode->name, name);
strcpy(aNode->telephone, telephone);
Then add to your aList
//program always go to this block, means `node` is always null
if (node == NULL) {
....
This is because the caller of the function passed aNode and it never changes within that loop. So it will always pass same value of aNode which is NULL.
I haven't looked at the logic of your code in details, but I think you may want to pass aList->head or rather you already pass aList so just use that.
I've read half a dozen answers with regards to this here and am relatively loathe to ask such a question, but I'm attempting to create a linked list using a struct in C, and was having some issues in passing pointers to the linked list. I think it's mostly sorted, but honestly am having acute issues trying to get the linked list working.
#include <stdio.h>
#include <stdlib.h>
typedef struct cell
{
int value;
struct cell *next;
} cell;
int inputplace = 0;
cell * createlist()
{
cell curElement = (cell *) malloc(sizeof(cell));
cell *head = &curElement;
cell *curEl = &curElement;
curEl->value = 900;
FILE *fp;
char *mode = "r";
fp = fopen("input",mode);
if(fp==NULL)
{
fprintf(stderr, "Unable to open input file 'input'");
exit(1);
}
int val;
int tempplace = 0;
while(tempplace < inputplace)
{
if(fscanf(fp, "%d", &val) != EOF)
{
tempplace++;
printf("%d", &val);
}
else
break;
}
while(fscanf(fp, "%d", &val)!=EOF)
{
inputplace++;
printf("%d\n", curEl);
if(val < 0)
{
curEl->value = -1;
curEl->next = -1;
break;
}
printf("%d\n", val);
curEl->value = val;
curEl->next = malloc(sizeof(struct cell));
curEl= curEl->next;
}
return head;
}
cell* reverse(cell* p)
{
cell * prev = -1;
cell * current = p;
cell * next;
while(current->value != -1)
{
next = current->next;
current->next = prev;
prev = current;
current = next;
}
return prev;
}
cell* append(cell* p, cell* q)
{
cell * current = p;
cell * r = p;
while(1)
{
if(current->value == -1)
{
current->value = q->value;
current->next = q->next;
}
}
return r;
}
int last(cell *p)
{
cell q = *p;
int last = -1;
while(1)
{
if(q.value == -1)
{
return last;
}
else
{
last = q.value;
q = *q.next;
}
}
}
cell * delete(int n, cell *p)
{
cell * head = p;
cell * prev = -1;
cell * current = p;
if(current-> value == n)
{
return current->next;
}
else
{
while(current->value != -1)
{
if(current->value==n)
{
prev->next = current->next;
break;
}
prev = current;
current = current->next;
}
}
return head;
}
int member(int n, cell *p)
{
cell q = *p;
while(1)
{
if(q.value == n)
{
return 1;
}
if(q.value == -1)
{
return 0;
}
q = *q.next;
}
}
int display(cell *p)
{
printf(" %c", '[');
cell q = *p;
while(1)
{
if(q.value == -1)
{
printf("%c ",']');
return 1;
}
if(q.next != p->next)
printf("%c ",',');
printf("%d", q.value);
q = *q.next;
}
printf("\n\n");
}
int main()
{
cell *head = createlist();
cell *headk = createlist();
cell *head3 = delete(5, head);
printf("%d, %d\n", head->value, head->next->value);
printf("Last head: %d\n", last(head));
display(headk);
display(head);
display(head3);
cell *head4 = delete(6, head);
display(head4);
cell *head5 = delete(7, head);
display(head5);
printf("Member2 6, head: %d\n", member(6,head));
printf("Member2 3, head: %d\n", member(3, head));
cell *head2 = reverse(head);
//print(head2);
printf("%d, %d\n", head2->value, head2->next->value);
}
So the input file contains numerical data with a negative one terminating the list:
Example input I'm using:
5
6
7
-1
1
2
3
-1
The issue I'm having is the second list is apparently overriding the first or some such, and my pointer-fu is weak, what do I need to do to successfully allocate the new structs?
Charles B.
You return a pointer to a local variable, and local variables goes out of scope once the function returns and that leaves you with a stray pointer. Using that stray pointer will lead to undefined behavior.
The problem starts with the declaration of curElement, and the compiler should really have screamed at you for that:
cell curElement = (cell *) malloc(sizeof(cell));
Here you declare curElement to be an actual structure, and not a pointer to the structure.
There's also the problem that you don't really have an end to the list. You allocate the next pointer of the last node you add, regardless if there's going to be a next node or not, and you don't initialize that node so the memory you allocate will be uninitialized, and trying to access it will lead to yet another undefined behavior.
I suggest something like the following abbreviated code:
cell *head = NULL;
cell *tail = NULL;
...
while (fscanf(fp, "%d", &val) == 1)
{
...
cell *current = malloc(sizeof(*current));
current->val = val;
current->next = NULL; // Very important!
// Check if this is the first node in the list
if (head == NULL)
head = tail = current;
else
{
// List is not empty, append node to end of list
tail->next = current;
tail = current;
}
}
Beside the change in how the list is handled and added to, there are also two other changes: The first is that the return value from the fscanf function is compared against 1, because fscanf (and family) will return the number of successfully parsed items, and this allows you to find format errors in the input file.
The second change is to not cast the return of malloc. In C you should never cast from or to void *, cast like that can hide subtle bugs.
I'm trying to implement a linked list abstraction, however I am running into problems. Once I create the linked list and add elements to it. When I print the list it only prints the first element in it in an infinite loop fashion, meaning that either the first element is linked to itself or the print function is incorrect. However, I can't find the problem, could someone help?
The following is the list abstraction:
typedef struct _friend {
char *firstname;
char *lastname;
char birthdate[9];
} friend;
typedef struct _node {
friend *value;
struct _node *next;
} node;
typedef struct _linkedlist {
node *head;
} linkedlist;
The program must follow this abstraction, as it is part of something bigger.
The following are the functions that should print the list and add a node to the beginning of the list:
/* addHead
*
* This function takes two parameters - a linked list and a friend.
* This creates a node for the linked list and connects the friend to the
* node. Then it adds the node to the head of the linked list.
*/
void addHead(linkedlist *llist, friend *f)
{
// create a node and put the friend in it
node *n = (node *)malloc(sizeof(node));
n->value = f;
n->next = NULL;
// if the list is empty
if (llist == NULL)
{
// this link is the entire list
llist->head = n;
printf("adding friend to null list\n");
}
// if the list is not empty
else
{
// make the new link's next pointer point to
// the first link in the list
n->next = llist->head;
printf("adding %s to head\n", n->value->firstname);
// make the head pointer point to the new link
llist->head = n;
}
}
/*
* printList
*
* This steps down through each of the nodes in a linked list and
* prints out the information stored in the friend to which the node points.
* Instead of automatically printing to the screen, it prints to the
* file pointer passed in. If the programmer wants to print to the screen,
* he/she will pass in stdout.
*/
void printList(linkedlist *llist,FILE *fp)
{
node *n;
friend *f;
// for each node, print out the friend attached to it
for(n = llist->head; n != NULL ; n = llist->head->next)
{
// assign f to the friend of the right node
f = n->value;
// print the friend out
fprintf(fp,"%s %s: %s\n",
f->firstname, f->lastname, f->birthdate);
}
}
Thank You
The for loop in printList isn't quite right:
for(n = llist->head; n != NULL ; n = llist->head->next)
This should read:
for(n = llist->head; n != NULL ; n = n->next)
Otherwise from the second iteration onwards, n gets set to the same value every single time.
The following isn't related to the problem you're having, but I thought I'd mention it anyway. In the following code:
if (llist == NULL)
{
// this link is the entire list
llist->head = n;
printf("adding friend to null list\n");
}
if llist == NULL, the llist->head = n will segfault.
With the current signature of addHead(), there's not a lot you can do if llist is NULL (other than printing an error message and bailing out).
If instead you meant to check whether llist->head is NULL, you don't need to do that since the else block already handles that correctly.
Try:
void printList(linkedlist *llist,FILE *fp)
{
node *n;
friend *f;
// for each node, print out the friend attached to it
for(n = llist->head; n != NULL ; n = n->next)
{
// assign f to the friend of the right node
f = n->value;
// print the friend out
fprintf(fp,"%s %s: %s\n",
f->firstname, f->lastname, f->birthdate);
}
}
I have done the following to your program:
slightly modified the friend structure. Declared firstname and lastname as arrays for convenience.
Wrote a main() which calls other functions
error checking in addHead()
added create_friend() function which creates friend struct
added freeList() to release the memory which was malloc()'ed
corrected looping error in your print function
So here it goes..
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct _friend {
char firstname[10];
char lastname[10];
char birthdate[9];
} friend;
typedef struct _node {
friend *value;
struct _node *next;
} node;
typedef struct _linkedlist {
node *head;
} linkedlist;
void addHead(linkedlist *llist, friend *f)
{
node *n = NULL;
if (( n = (node *)malloc(sizeof(node))) == NULL) {
printf("unable to allocate memory \n");
exit(1);
}
n->value = f;
n->next = NULL;
if (llist == NULL) {
llist->head = n;
printf("adding friend to null list\n");
} else {
n->next = llist->head;
printf("adding %s to head\n", n->value->firstname);
llist->head = n;
}
return;
}
void printList(linkedlist *llist)
{
node *n;
friend *f;
if (llist->head == NULL) {
printf("Empty list \n");
return;
}
for(n = llist->head; n != NULL ; n = n->next) {
f = n->value;
printf("%s %s %d \n", f->firstname, f->lastname, f->birthdate);
}
return;
}
friend * create_friend(char *fn, char *ln, char *dob)
{
friend *fp = NULL;
if ((fp = malloc(sizeof(friend))) == NULL) {
printf("unable to allocate memory \n");
exit(1);
}
strcpy(fp->firstname, fn);
strcpy(fp->lastname, ln);
strcpy(fp->birthdate, dob);
return fp;
}
void freeList(linkedlist *llist)
{
node *cur = llist->head;
node *prev = cur;
friend *f;
while (cur != NULL) {
prev = cur;
cur = cur->next;
f = prev->value;
printf("freeing .. %s %s %d \n", f->firstname, f->lastname, f->birthdate);
free(prev->value);
free(prev);
}
return;
}
int main(void)
{
linkedlist ll;
friend *f;
ll.head = NULL;
f = create_friend("firstname1", "lastname1", "12345678");
addHead(&ll, f);
f = create_friend("firstname2", "lastname2", "12345678");
addHead(&ll, f);
f = create_friend("firstname3", "lastname3", "12345678");
addHead(&ll, f);
printList(&ll);
freeList(&ll);
ll.head = NULL;
printList(&ll);
return 0;
}
Hope this helps!
Should be n = n ->next otherwise you're just getting the next of the head every time.