Library is empty then seg fault - c

Hey guys im doing a project and I keep getting "Library is empty" and then a seg fault, running trough this with gdb its telling me that the error is in my count_list function. But i cannot seem to figure out why? any tips would be appreciated its killing me to be so close to finished and get hung up on this.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Assume max char count in file */
#define MAX 20
/* library struct - DO NOT CHANGE */
typedef struct library
{
char *genre;
char *band;
char *album;
float rating;
struct library *next;
}Library;
/* Function Prototypes - DO NOT CHANGE */
Library *Read_File(FILE *);
void Print(Library *);
Library *Create_List(Library *, Library *);
Library *Create_Album(char *, char *, char *, float);
Library *Merge(Library *, Library *);
int Count_List(Library *);
void Split_List(Library *, Library **, Library **);
Library *Merge_Sort(Library *);
Library *Delete_Genre(Library *);
void Free_Entry(Library *);
Library *Clean(Library *);
/* MAIN
* Error check file parameter
* Call Read_File to fill our list
* Print our list
* Merge Sort the linked list (by genre)
* Delete a genre
* Free the list
*/
int
main(int argc, char **argv)
{
if(argc != 2)
{
printf("Not enough arguments.\n");
return 0;
}
FILE *fp = NULL;
if((fp = fopen(argv[1], "r")) == NULL)
{
printf("File can not be opened.\n");
return 0;
}
Library *Head = NULL;
Head = Read_File(fp);
Print(Head);
Merge_Sort(Head);
Print(Head);
Head = Delete_Genre(Head);
Print(Head);
Head = Clean(Head);
Print(Head);
return 0;
}
/* Clean()
* Delete the linked list, recursively
*/
Library *
Clean(Library *Head)
{
if(Head) return NULL;
Library *Tmp = Head->next;
Free_Entry(Head);
Clean(Tmp->next);
}
/* Free_Entry()
* wrapper function to free a struct Library
*/
void
Free_Entry(Library *Entry)
{
free(Entry);
}
/* Delete_Genre()
* Deletes a genre inputted by user
* Logic:
* prompt user for genre input
* traverse list deleting all structs that contain the genre
*/
Library *
Delete_Genre(Library *Head)
{
if(!Head)
{
printf("List Empty.\n");
return NULL;
}
char *input = malloc(MAX * sizeof(char *));
Library *Current = Head;
Library *Tail = NULL;
printf("Which genre would you like to delete?\n");
scanf("%s", input);
while(Current)
{
if(strcmp(Current->genre, input))
{
if(Current = Head)
{
Head = Head->next;
Free_Entry(Current);
Current = Head;
}
else
Tail->next = Current->next;
}
else
Current = Current->next;
}
}
/* Read_File()
* Open file fp
* Create a struct from information in text file
* Link our list with newly created struct
*/
Library *
Read_File(FILE *fp)
{
Library *Head, *Entry;
Head = Entry = NULL;
char *genre, *band, *album;
float rating;
while(1)
{
fscanf(fp, "%s %s %s %f", &genre, &band, &album, &rating);
if(!feof(fp))
break;
Entry = Create_Album(genre, band, album, rating);
Head = Create_List(Entry, Head);
}
return Head;
}
/* Print()
* Print the linked list
*/
void
Print(Library *Head)
{
if(!Head)
{
printf("Library is empty.\n");
return;
}
while(Head)
{
printf("%20s %20s %20s %20.2f \n",
Head->genre, Head->band, Head->album, Head->rating);
Head = Head->next;
}
printf("\n\n");
//return Head;
}
/* Create_Album
* Create a struct and assign the given args to it as appropriate
*/
Library *
Create_Album(char *genre, char *band, char *album, float rating)
{
Library *Entry = malloc(sizeof(Library));
strcpy(Entry->genre, genre);
strcpy(Entry->band, band);
strcpy(Entry->album, album);
Entry->rating = rating;
Entry->next = NULL;
return Entry;
}
/* Create_List()
* Push Entry onto our List
*/
Library *
Create_List(Library *Head, Library *Entry)
{
if(!Head)
return Entry;
Entry->next = Head;
return Entry;
}
/* Merge_Sort()
* Recursively split our list between Left and Right
* Merge our Left and Right lists
*/
Library *
Merge_Sort(Library *Head)
{
Library *Tmp = Head;
Library *Left, *Right, *Result;
Left = Right = Result = NULL;
int count = Count_List(Head);
if(count = 1)
return Tmp;
Left = Merge_Sort(Left);
Right = Merge_Sort(Right);
Result = Merge(Left, Right);
return Result;
}
/* Split_List()
* split our list in half
*/
void
Split_List(Library *Head, Library **Left, Library **Right)
{
int size = Count_List(Head);
int i;
Library *Tmp = Head;
*Left = Head;
for(i=1; i<size/2; ++i)
Tmp=Tmp->next;
*Right = Tmp->next;
Tmp->next = NULL;
}
/* Merge()
* Merge two linked lists Left and Right together in sorted order
*/
Library *
Merge(Library *Left, Library *Right)
{
Library *Result, *Tmp;
Result = Tmp = NULL;
if(strcmp(Left->genre, Right->genre) <= 0)
{
Result = Left;
Left = Left->next;
}
else
{
Result = Right;
Right = Right->next;
}
Tmp = Result;
while(Left != NULL && Right != NULL)
{
if(Left != NULL && Right!= NULL)
{
if (strcmp(Left->genre, Right->genre) <= 0)
{
Tmp->next = Left;
Left = Left->next;
}
else
{
Tmp->next = Right;
Right = Right->next;
}
Tmp = Tmp->next;
}
}
return Result;
}
/* Count_List()
* Count the number of elements in a linked list
*/
int
Count_List(Library *Head)
{
Library *Tmp = Head;
int count = 0;
while(Tmp->next != NULL)
{
count++;
Tmp = Tmp->next;
}
}

Observations,
Count_List() should count each element on list, your version only counts starting at the second item on the list. And it should always return an int,
int
Count_List(Library *Head)
{
int count = 0;
Library *Tmp = Head;
if(!Tmp) return(count);
while(Tmp != NULL)
{
count++;
Tmp = Tmp->next;
}
return(count);
}
Inside Read_File() you call Create_List(Entry,Head), but your function signature for Create_List(Head,Entry); which do you intend? Probably (Head,Entry). Use fgets and sscanf to Read_File,
Library *
Read_File(FILE *fp)
{
Library *Head, *Entry;
Head = Entry = NULL;
char genre[100], band[100], album[100];
float rating;
char line[100];
while(fgets(line,sizeof(line),fp)
{
sscanf(line,"%s %s %s %f", genre, band, album, &rating);
Entry = Create_Album(genre, band, album, rating);
Head = Create_List(Head,Entry);
}
return Head;
}
Looking at Create_List(), you seem to be implementing the List as a stack (push Entry onto head of list),
Library *
Create_List(Library *Head, Library *Entry)
{
if(!Head)
return Entry;
Entry->next = Head;
return Entry;
}
Create_Album() needs to check for successful malloc before assigning values to member variables,
Library *Entry=NULL;
if( !(Entry=(Library*)malloc(sizeof(Library))) )
{
printf("error creating album\n");fflush(stdout);
return Entry;
}
Speaking of Library struct, you declare the members genre, band, album as pointers, but you need space to copy memory, example,
typedef struct library
{
char genre[50];
char band[50];
char album[50];
float rating;
struct library *next;
}Library;
My suggestion would be to build constructor and destructor functions LibraryNew, LibraryDel.
Check for (argc<2) arguments (your message says not enough, instead of need 2,
if(argc < 2)
{
printf("Not enough arguments.\n");
return 0;
}
And that fixes the biggest problems,
./library music.x 2
line:Rock Antrax Party 1.2
Rock,Antrax,Party,1.200000
Rock,Antrax,Party,1.200000
added: Rock,Antrax,Party,1.200000
Rock Antrax Party 1.20
Rock Antrax Party 1.20
Which genre would you like to delete?

your function doesn't return a number (so some unexpected value would be really returned to the caller). you need to add the return statement.
int Count_List(Library *Head)
{
Library *Tmp = Head;
int count = 0;
while(Tmp->next != NULL)
{
count++;
Tmp = Tmp->next;
}
}
some other issues you would get if you turn on the warning option of your compiler:
if(Current = Head) would you expect == ?
fscanf(fp, "%s %s %s %f", &genre, &band, &album, &rating);
should be
fscanf(fp, "%s %s %s %f", genre, band, album, &rating);
if(count = 1) // == ?
Delete_Genre() /Clean() don't have return

You forgot to actually return something from Count_List.
int Count_List(Library *Head)
{
Library *Tmp = Head;
int count = 0;
while(Tmp->next != NULL)
{
count++;
Tmp = Tmp->next;
}
return count;
}

Related

Creating a singly linked list from a .txt file and reversing odd numbers of each line in C

I have a project about linked lists but I'm having a hard time doing it. The teacher wants me to read a .txt file and create singly linked list from it. After that, I need to reverse odd numbers of every line. Then print it. Here is the code which I used for printing the linked list. But I need help to reverse the odd numbers of each line.
This is the code which I used to print the list:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct list {
char *string;
struct list *next;
};
typedef struct list LIST;
int main(void) {
FILE *fp;
char line[10];
LIST *current, *head;
head = current = NULL;
fp = fopen("data.txt", "r");
while(fgets(line, sizeof(line), fp)){
LIST *node = malloc(sizeof(LIST));
node->string = strdup(line);
node->next =NULL;
if(head == NULL){
current = head = node;
} else {
current = current->next = node;
}
}
fclose(fp);
for(current = head; current ; current=current->next){
printf("%s", current->string);
}
return 0;
}
Here is the content of the .txt file:
10
9,6,11,7,12,18,19,14,15,13
13,14,9,12,15,3,18,20,1,2
4,11,8,17,12,15,20,10,3,16
19,4,11,1,13,17,12,16,20,18
1,6,20,11,13,9,7,16,10,2
12,4,11,16,3,20,9,19,17,15
20,3,10,12,18,2,5,14,15,16
18,19,15,2,6,9,1,3,17,4
7,6,20,1,11,4,3,5,8,16
1,2,16,13,17,10,12,9,4,15
"But I need help to reverse the odd numbers of each line."
There are several other parts that need to be considered before this step can be developed.
Following are suggestions for a functions approach implementation using your problem description. A few items are simply suggestions to simplify the existing code. And a few other steps, are not mentioned as necessary, but should be considered:
Since you are not mandated to use char *string; in your problem description, choose to use a reasonable string length variable that does not require an additional layer of dynamic allocation, such as char string[260]; (or even smaller to fit your input file.) This will greatly simplify the code.
Because the input file is sized with lines ~30 char long, declare the variable line to be at least large enough to contain one line, eg 80 would allow larger values, and still allow enough space, but since memory is cheap, go with the same size as is used in the string member of your linked list.
Move the work of populating each new node to a function. It also will greatly simplify the program, and provide greater readability. Eg: void insert(LIST **head_ref, char *str);
Always test the return of fopen() before attempting to use the file descriptor.
To manipulate the contents of each odd row (eg 1, 3, 5, 7, 9), as numbers, the contents of each line read in from a file as a string, needs to first be converted to a collection of numbers. This suggests an additional member be added to the struct. For example int num[10].
The previous observation implicitly suggests the need of an additional function to parse and convert each comma delimited string into discrete integer values. Perhaps with the prototype: void parseIntArray(LIST **list);
The next and final task also suggests an additional function to reverse the contents of selected array member integer arrays. This one might use a prototype such as: void reverse_odd(LIST **list, size_t size);
Finally, because each node of LIST created required dynamically allocated memory, once finished using LIST, the memory must be given back to the OS to prevent memory leaks. An additional function to do this could be prototyped: void freeList(LIST **head);
Following are the main() function and preceding support declarations etc. It is intended here to illustrate the above suggested steps, and the benefits of breaking down a bigger problem into smaller problems, then implementing each smaller solution to support the whole. Benefits include for example readability and maintainability and potential re-use of code-base, (Note the similarity of argument lists in each supporting function.):
#define MAX_STRLEN 260 //use mnemonic values to avoid magic numbers in code
struct list {
char string[MAX_STRLEN];
int arr[10];
struct list *next;
};
typedef struct list LIST;
//Prototypes of 'smaller' solutions
void insert(LIST **head_ref, char *str);
void parseIntArray(LIST **list);
void reverse_odd(LIST **list, size_t size);
void freeList(LIST **head);
int main(void)
{
FILE *fp;
char line[MAX_STRLEN];
LIST *current, *head;
char *convPtr = NULL;
head = current = NULL;
fp = fopen("data.txt", "r");
if(fp)
{
//consume 1st line
if(fgets(line, sizeof(line), fp));//10
{
sizeArray = strtol(line, &convPtr, 10);
if(errno != ERANGE)
{
while(fgets(line, sizeof(line), fp))
{
//(see implementations of each below)
//create new node, insert num string
insert(&current, line);
//convert new->string to integers, place in new->array
parseIntArray(&current);
//reverse 'odd' contents of each array
reverse_odd(&current, sizeArray);
}
}else{//handle error and leave}
}
fclose(fp);
}else{//handle error and leave}
//At this point in code, entire file is captured into nodes of list.
//use list as needed
//When finished using list, memory must be freed to prevent memory leaks
head = current;
freeList(&head);
return 0;
}
The remaining code segments are the function implementations used above:
void freeList(LIST **head)
{
LIST *tmp;
while (*head != NULL)
{
tmp = (*head);
(*head) = (*head)->next;
free(tmp);
}
}
//create new node, insert num string
void insert(LIST **head_ref, char *str)
{
int *arr = malloc(numNodes * sizeof(*arr));
//allocate node
LIST* new = calloc(1, sizeof(*new));
//put in the data
strcpy(new->string, str);
//Make next of new node as head
new->next = (*head_ref);
//Move the head to point to the new node
(*head_ref) = new;
}
//convert new->string to integers, place in list->array
void parseIntArray(LIST **list)
{
char *tok = NULL;
int i = 0;
int tmp = 0;
char *sArray = strdup((*list)->string);
tok = strtok(sArray, ",\n ");
while(tok)
{
errno = 0;
tmp = atoi(tok);
if(errno == ERANGE)
{
printf("Error converting string to number\nExiting.");
return;
}
(*list)->arr[i] = tmp;
i++;
tok = strtok(NULL, ",\n ");
}
}
//reverse 'odd' contents of list->array
void reverse_odd(LIST **list, size_t size)
{
int *ptr = &((*list)->arr[0]);
int *tmp = malloc(size * sizeof(*tmp));
memset(tmp, -1, size*sizeof(*tmp));
for(int i=0;i<size;i++)
{
if(ptr[i]%2 != 0)
tmp[size-1-i] = ptr[i];
}
for(int i=0;i<size;i++)
{
if(tmp[i] < 0)
{
while((*ptr)%2 != 0 ) ptr++;
tmp[i] = *ptr;
ptr++;
}
}
memcpy((*list)->arr, tmp, size*sizeof(int));
}
This hope this code will do the job.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct line {
struct num *first;
struct line *next;
} LineNode;
typedef struct num {
int num;
int order;
struct num *next;
} NumNode;
int main() {
FILE *fp;
char ch;
int counter = 0;
NumNode *curr_num, *even_ptr, *odd_ptr, *odd_head, *even_head;
LineNode *curr_line, *line_head;
curr_num = even_head = odd_head = even_ptr = odd_ptr = NULL;
line_head = curr_line = NULL;
fp = fopen("data.txt", "r");
if (fp == NULL)
{
return 1;
}
ch = fgetc(fp);
while(ch != EOF){
if (ch >= 48 && ch <= 57)
{
int n = 0;
while (ch != EOF && ch != '\n' && ch >= 48 && ch <= 57)
{
int x = ch - 48;
n = n * 10 + x;
ch = fgetc(fp);
}
NumNode *node = malloc(sizeof(NumNode));
node->num = n;
node->order = counter;
node->next =NULL;
if (n % 2 == 0){
if(even_head == NULL){
even_head = even_ptr = node;
} else {
even_ptr = even_ptr->next = node;
}
}else{
if(odd_head == NULL){
odd_head = node;
} else {
node->next = odd_head;
odd_head = node;
}
}
counter++;
}
if (ch == '\n' || ch == EOF)
{
NumNode *num_node, *head;
num_node = head = NULL;
even_ptr = even_head;
odd_ptr = odd_head;
counter = 0;
if (even_head != NULL && even_head->order == counter){
head = num_node = even_ptr;
even_ptr = even_ptr->next;
} else {
head = num_node = odd_ptr;
odd_ptr = odd_ptr->next;
}
counter++;
while (even_ptr != NULL)
{
if (even_ptr->order == counter) {
num_node = num_node->next = even_ptr;
even_ptr = even_ptr->next;
}
else if (odd_ptr != NULL) {
num_node = num_node->next = odd_ptr;
odd_ptr = odd_ptr->next;
}
counter++;
}
while (odd_ptr != NULL)
{
num_node = num_node->next = odd_ptr;
odd_ptr = odd_ptr->next;
}
LineNode *node = malloc(sizeof(LineNode));
node->next =NULL;
node->first = head;
if (line_head == NULL)
line_head = curr_line = node;
else
curr_line = curr_line->next = node;
odd_head = even_head = NULL;
counter = 0;
}
ch = fgetc(fp);
}
fclose(fp);
for(curr_line = line_head; curr_line != NULL ; curr_line=curr_line->next) {
for(curr_num = curr_line->first; curr_num != NULL ; curr_num=curr_num->next) {
printf("%d", curr_num->num);
if (curr_num->next != NULL)
printf(",");
}
printf("\n");
}
return 0;
}

How to insert data in linked structure

My insertion worked, so a new question, How do I set a data to NULL in C? Pls see the last part for illustration.!
I have defined a structure type
typedef struct node {
char* data;
int weight;
bool end_of_key;
struct node * left;
struct node * equal;
struct node * right;
} node_t;
int main(int argc, char *argv[]){
node_t* root=NULL;
int weight;
int i = 1;
insert(root,"cat",3);
insert(root,"at",2);
insert(root,"cute",4);
.....
return 0 ;}
This is my insert function
node_t* insert(node_t* pNode,char* word, int weight) {
/**
* Create a new pNode, and save a character from word
*/
pNode = (node_t*)malloc(sizeof(node_t));
if(*pNode->data == NULL){
pNode->left = NULL;
pNode->equal = NULL;
pNode->right = NULL;
pNode->data = word;
}
if (word[0] < *(pNode->data)) {
/**
* Insert the character on the left branch
*/
pNode->left = insert(pNode, word, weight);
}
else if (word[0] == *(pNode->data)) {
if ((word[1]) == '\0') {
/**
*set pNode end_of_key_flag to true and assign weight
*/
pNode->end_of_key = true;
pNode->weight = weight;
// printf("%c", *(pNode->data++));
}
else {
/**
* If the word contains more characters, try to insert them
* under the equal branch
*/
// printf("%c", *(pNode->data++));
pNode->equal = insert(pNode,word + 1, weight);
}
}
else {
/**
* If current char in word is greater than char in pData
* Insert the character on the right branch
*/
pNode->right = insert(pNode,word, weight);
}
return pNode;}
this code is trying to do this
So my insertion finally worked but it appears that it can only insert one thing,I am wondering how do I set data to NULL in C?
if(*pNode->data == NULL){
pNode->left = NULL;
pNode->equal = NULL;
pNode->right = NULL;
pNode->data = word;
}
I want to run this four lines of code when *pNode->data is empty but it apparently did not work the way I wanted it to.
Some improvements of your code
insert() must have first parameter to be node_t ** (see comments)
char *data must be char data, because every node contains only one char
weight can be calculated when the list is filled up
Here is corrected version of your code. Function get() is used to find the key in the filled up list (for testing).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct node {
char data;
int weight;
struct node * left;
struct node * equal;
struct node * right;
} node_t;
void insert(node_t** pNode, char* word, int weight)
{
char data;
node_t **pNext;
node_t *pCurrent;
if (word == NULL)
{
return ;
}
data = word[weight];
if (*pNode == NULL)
{
*pNode = malloc(sizeof(node_t));
pCurrent = *pNode;
memset(pCurrent, 0, sizeof(node_t));
pCurrent->data = data;
}
else
{
pCurrent = *pNode;
}
if (data == pCurrent->data)
{
weight ++;
if (strlen(word) == weight)
{
pCurrent->weight = weight;
return;
}
pNext = &pCurrent->equal;
}
else if (data > pCurrent->data)
{
pNext = &pCurrent->right;
}
else
{
pNext = &pCurrent->left;
}
insert(pNext, word, weight);
}
int get(node_t** pNode, char *word, int weight)
{
char data;
node_t **pNext;
node_t *pCurrent;
if (word == NULL)
{
return 0;
}
data = word[weight];
if (*pNode == NULL)
{
return 0; // not found
}
pCurrent = *pNode;
if (data == pCurrent->data)
{
weight ++;
if (strlen(word) == weight)
{
return pCurrent->weight;
}
pNext = &pCurrent->equal;
}
else if (data > pCurrent->data)
{
pNext = &pCurrent->right;
}
else
{
pNext = &pCurrent->left;
}
return get(pNext, word, weight);
}
int main()
{
node_t * root = NULL;
insert(&root, "cat", 0);
insert(&root, "at", 0);
insert(&root, "cute", 0);
printf("cat=%d\n",get(&root,"cat",0)); // cat=3
printf("at=%d\n",get(&root,"at",0)); // at=2
printf("cute=%d\n",get(&root,"cute",0)); // cute=4
// todo: free memory
return 0;
}
The code is tested except freeing the memory.
First, there is something wrong with your insert() signature
(as already pointed by #MichaelWalz )
you'd rather
node_t* insert(node_t** pNode, char* word, int weight);
and then
insert(&root,"cute",4);
why don't you start fixing this and edit your post ?

Need to Merge Sort a Linked List of names

I need help with my merge sort function that is a linke list of names. I get a seg fault when I run the program, and I feel like something isnt right. Thanks for the help in advance.
The program is supposed to print out the list of names before sorting, then the list of names after sorting.
#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
#include<string.h>
#define MAX_STR_LEN 25
typedef struct Data_ {
char *name;
struct Data_ *next;
}Data;
void split_in_half(Data *source, Data **frontRef,Data **backRef);
Data* merge(Data *a, Data *b);
void merge_sort(Data **list);
Data* read_from_file(const char* file, const int size);
void display(Data *list);
void push(Data **head, char *name);
int main(int argc, char **argv){
if(argc != 2){
printf("Not enough parameters!");
exit(0);
}
Data *head = NULL;
int size = 10;
head = read_from_file(argv[1], size);
printf("\nBefore sort");
display(head);
printf("\nMerge Sort\n");
merge_sort(&head);
display(head);
}
void merge_sort(Data **list){
Data *head = *list;
Data *temp;
Data *temp2;
if((head == NULL) || (head->next == NULL))
{
return;
}
split_in_half(head, &temp, &temp2);
head = merge(temp, temp2);
}
Data *merge(Data *a, Data *b){
Data *result = NULL;
if(a == NULL)
return(b);
else if(b==NULL)
return (a);
if(strcmp(a->name, b->name) > 0)
{
result = a;
result->next = merge(a->next, b);
}
else
{
result = b;
result->next = merge(a, b->next);
}
return (result);
}
void split_in_half(Data *source, Data **frontRef,Data **backRef){
Data *fast;
Data *slow;
if(source == NULL || source->next == NULL)
{
*frontRef = source;
*backRef = NULL;
}
else
{
slow = source;
fast = source->next;
while(fast != NULL)
{
fast = fast->next;
if(fast != NULL)
{
slow = slow->next;
fast = fast->next;
}
}
}
*frontRef = source;
*backRef = slow->next;
slow->next = NULL;
}
void push(Data **head, char *name){
Data *temp = malloc(sizeof(Data));
temp->name = strdup(name);
temp->next = *head;
*head = temp;
}
Data* read_from_file(const char* file, const int size){
FILE *input;
input = fopen(file, "r");
Data *new_ = (Data*)malloc(sizeof(Data*));
new_->next = NULL;
int i;
char name[MAX_STR_LEN];
for(i = 0; i < size; i++){
fscanf(input, "%24s", &name);
push(&new_, name);
}
return new_;
}
void display(Data *list){
Data *current = list;
while(current->next != NULL){
printf("\n%s", current->name);
current = current->next;
}
}
The file that I read in is a list of names. It is:
Derek
Drew
Randell
Terrell
Carmen
Colin
Eddy
Pablo
Lamont
Dexter
In read_from_file you have:
Data *new_ = (Data*)malloc(sizeof(Data*));
new_->next = NULL;
However, you have only allocated space for a pointer, rather than allocated space for a Data structure. So you will be writing off the end of the allocated space in the second line above.
Instead, write:
Data *new_ = (Data*)malloc(sizeof(Data));
new_->next = NULL;
[ You can read about the cons of casting the result of malloc at Do I cast the result of malloc?, but I am just making the minimal change for progress above ]
Once you've made the fix above, try walking through the program in a debugger, or adding printf statements, so you can see exactly how the program is behaving, and understand where any other problems occur.

All Nodes in a linked list point to same object

The problem is somewhere in here....
char buffer[80];
char *name;
while (1) {
fgets(buffer, 80, inf); //reads in at most 80 char from a line
if (feof(inf)) //this checks to see if the special EOF was read
break; //if so, break out of while and continue with your main
name = (char *) malloc(sizeof(char)*20);
....
name = strtok(buffer, " ");//get first token up to space
stock = newStock(name,...)
....
}
I'm working in C with generic linked lists. I made a list implementation that I've tested and know works with chars. I'm trying to add stocks (I created a stock struct) to the linked list, with each node of the linked list holding a stock struct, but when I finish reading in the stocks all of the nodes point to the same struct and I can't figure out why. Here's some snippets of my code
list *list = malloc(sizeof(list));
newList(list, sizeof(stock_t));
while(1) {
...
(read from file)
...
stock_t *stock;
stock = newStock(name, closes, opens, numshares, getPriceF, getTotalDollarAmountF,getPercentChangeF,toStringF);
addToBack(list, stock);
}
Here's the newStock function:
stock_t *newStock(char *name, float closingSharePrice, float openingSharePrice, int numberOfShares, getPrice getP, getTotalDollarAmount getTotal, getPercentChange getPercent, toString toStr) {
stock_t *stock = malloc(sizeof(stock));
stock->stockSymbol = name;
stock->closingSharePrice = closingSharePrice;
stock->openingSharePrice = openingSharePrice;
stock->numberOfShares = numberOfShares;
stock->getP = getP;
stock->getTotal = getTotal;
stock->getPercent = getPercent;
stock->toStr = toStr;
return stock;
}
In a way I see what's wrong. newStock returns a new pointer every time, but it always gets stored in the variable 'stock' which is what every node points to, so it's going to be equal to whatever the last pointer newStock returned was...but I don't see the way around this. I tried having newStock return just a stock_t, and doing addToBack(list, &stock), but that didn't solve the problem either.
Any help would be appreciated!
Here is some code from the list:
typedef struct node {
void *data;
struct node *next;
}node_t;
typedef struct {
int length;
int elementSize;
node_t *head;
node_t *tail;
} list;
void newList(list *list, int elementSize) {
assert(elementSize > 0);
list->length = 0;
list->elementSize = elementSize;
list->head = list->tail = NULL;
}
void addToBack(list *list, void *element) {
node_t *node = malloc(sizeof(node_t));
node->data = malloc(list->elementSize);
node->next = NULL; //back node
memcpy(node->data, element, list->elementSize);
if (list->length == 0) { //if first node added
list->head = list->tail = node;
}
else {
list->tail->next = node;
list->tail = node;
}
list->length++;
}
Here's code from the stock struct:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
typedef float (*getPrice)(void *S);
typedef float (*getTotalDollarAmount)(void *S);
typedef float (*getPercentChange)(void *S);
typedef char *(*toString)(void *S);
typedef struct stock{
char *stockSymbol;
float closingSharePrice;
float openingSharePrice;
int numberOfShares;
getPrice getP;
getTotalDollarAmount getTotal;
getPercentChange getPercent;
toString toStr;
}stock_t;
The generic functions probably seem like overkill but this is for homework (if you couldn't tell already) so we were asked to specifically use them. I don't think that has anything to do with the problem though.
Here are the definitions for those functions anyway
float getPriceF(void *S) {
stock_t *stock = (stock_t*)S;
return stock->closingSharePrice;
}
float getTotalDollarAmountF(void *S) {
stock_t *stock = (stock_t*)S;
return ((stock->closingSharePrice) * (stock->numberOfShares));
}
float getPercentChangeF(void *S) {
stock_t *stock = (stock_t*)S;
return ((stock->closingSharePrice - stock->openingSharePrice)/(stock->openingSharePrice));
}
char *toStringF(void *S) {
stock_t* stock = (stock_t*)S;
char *name = malloc(20*sizeof(char));
//sprintf(name, "Symbol is: %s. ", (stock->stockSymbol));
return stock->stockSymbol;
}
void printStock(void *S) {
char *str = toStringF(S);
printf("%s \n", str);
}
And this is how I'm traversing the list:
typedef void (*iterate)(void *); //this is in the list.h file, just putting it here to avoid confusion
void traverse(list *list, iterate iterator) {
assert(iterator != NULL);
node_t *current = list->head;
while (current != NULL) {
iterator(current->data);
current = current->next;
}
}
And then in my main I just called
traverse(list, printStock);
I can't find any problems with your code (that would cause your problem, anyway - there are places where you don't check the return from malloc() and stuff like that, but those are not relevant to this question). You don't supply the definition of stock_t, so I made a new data struct, and a new couple of functions, otherwise I just copied and pasted the code you provided:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
/* Your code starts here */
typedef struct node {
void *data;
struct node *next;
}node_t;
typedef struct {
int length;
int elementSize;
node_t *head;
node_t *tail;
} list;
void newList(list *list, int elementSize) {
assert(elementSize > 0);
list->length = 0;
list->elementSize = elementSize;
list->head = list->tail = NULL;
}
void addToBack(list *list, void *element) {
node_t *node = malloc(sizeof(node_t));
node->data = malloc(list->elementSize);
node->next = NULL; //back node
memcpy(node->data, element, list->elementSize);
if (list->length == 0) { //if first node added
list->head = list->tail = node;
}
else {
list->tail->next = node;
list->tail = node;
}
list->length++;
}
/* Your code ends here */
/* I made a new struct, rather than stock, since you didn't supply it */
struct mydata {
int num1;
int num2;
};
/* I use this instead of newStock(), but it works the same way */
struct mydata * newNode(const int a, const int b) {
struct mydata * newdata = malloc(sizeof *newdata);
if ( newdata == NULL ) {
fputs("Error allocating memory", stderr);
exit(EXIT_FAILURE);
}
newdata->num1 = a;
newdata->num2 = b;
return newdata;
}
/* I added this function to check the list is good */
void printList(list * list) {
struct node * node = list->head;
int n = 1;
while ( node ) {
struct mydata * data = node->data;
printf("%d: %d %d\n", n++, data->num1, data->num2);
node = node->next;
}
}
/* Main function */
int main(void) {
list *list = malloc(sizeof(list));
newList(list, sizeof(struct mydata));
struct mydata * data;
data = newNode(1, 2);
addToBack(list, data);
data = newNode(3, 4);
addToBack(list, data);
data = newNode(5, 6);
addToBack(list, data);
printList(list);
return 0;
}
which outputs this:
paul#MacBook:~/Documents/src$ ./list
1: 1 2
2: 3 4
3: 5 6
paul#MacBook:~/Documents/src$
demonstrating that you have a 3 node list, with all nodes different and where you'd expect them to be.
Either there is some other problem in code you're not showing, or for some reason you are thinking each node points to the same struct when it actually doesn't.
One possibility is that you have a char * data member in your stock struct. It's impossible to tell from the code you provided, but it's possible that you really are creating different nodes, but they all end up pointing to the same name, so they just look like they're the same. If you're assigning a pointer to name, you should make sure it's freshly allocated memory each time, and that you're not just, for instance, strcpy()ing into the same memory and assigning the same address to each stock struct.
EDIT: Looks like that was your problem. This:
name = (char *) malloc(sizeof(char)*20);
....
name = strtok(buffer, " ");
should be:
name = (char *) malloc(sizeof(char)*20);
....
strcpy(name, strtok(buffer, " "));
Right now, you malloc() new memory and store a reference to it in name, but then you lose that reference and your memory when you overwrite it with the address returned from strtok(). Instead, you need to copy that token into your newly allocated memory, as shown.

Linked list in C, is the list being constructed correctly?

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.

Resources