I am currently having trouble trying to print my doubly linked list out. The problem is either in the getSongInfo function or in the printSongInfo function. I am very new to coding and all the help is greatly appreciated. I also don't know how to use the tail pointer when using a doubly linked list.
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<malloc.h>
#pragma warning(disable : 4996)
#pragma warning(disable : 6387)
#pragma once
//structs
typedef struct {
char* title;
char* artist;
int rating;
SongNode* next;
SongNode* prev;
}SongNode;
//Constants
#define kInfiniteLoop 1
#define kStringLength 30
//Prototypes
int getNum(void);
void eliminateEndOfLine(char* buffer);
void printSongInfo(SongNode* head);
SongNode* getSongInfo(SongNode** head, SongNode** tail, char title[], char artist[], int rating);
int main() {
char title[kStringLength];
char artist[kStringLength];
int rating = 0;
SongNode* head = NULL;
SongNode* tail = NULL;
printf("Enter Title, Artist and Rating\n");
printf("Enter'.' to get out of the loop and print list\n");
while (kInfiniteLoop) {
printf("Title: ");
fgets(title, kStringLength, stdin);
eliminateEndOfLine(title);
if (strcmp(title, ".") == 0) {
break;
}
printf("Artist: ");
fgets(artist, kStringLength, stdin);
eliminateEndOfLine(artist);
printf("Rating: ");
while (rating = getNum()) { // error check the rating this will check to make sure the rating is in the range 1-5.
if (rating < 1 || rating>5) {
printf("The number you have entered is invaild\n");
rating = 0;
printf("Rating: ");
continue;
}
break;
head=getSongInfo(&head, &tail, title, artist, rating);
printf("\n");
}
}
printSongInfo(head);
return 0;
}
/*===============================================================================================================*/
/*FUNCTION :getNum(void) */
/*PARAMETERS :void */
/*RETURNS :number */
/*DESCRIPTION:This function is the user input function to get a number rating */
/*===============================================================================================================*/
int getNum(void)
{/* the array is 121 bytes in size; we'll see in a later lecture how we can improve this code */
char record[121] = { 0 }; /* record stores the string */
int number = 0;
/* NOTE to student: indent and brace this function consistent with your others */
/* use fgets() to get a string from the keyboard */
fgets(record, 121, stdin);
/* extract the number from the string; sscanf() returns a number
* corresponding with the number of items it found in the string */
if (sscanf(record, "%d", &number) != 1)
{
/* if the user did not enter a number recognizable by
* the system, set number to -1 */
number = -1;
}
return number;
}
/*=======================================================================================================*/
/*FUCNTION :void eliminateEndOfLine */
/*PARAMETER :(char* buffer) */
/*RETURNS :void */
/*DESCRIPTION :This function takes a pointer to a string and looks through the string to find the */
/* newline.It takes the new line out of the string. */
/*=======================================================================================================*/
void eliminateEndOfLine(char* buffer)
{
char* target = strchr(buffer, '\n');
if (target != NULL)
{
*target = '\0';
}
}
SongNode* getSongInfo(SongNode** head, SongNode** tail, char title[], char artist[], int rating)
{
SongNode *newBlock = NULL;
newBlock = (SongNode*)malloc(sizeof(SongNode));
if (newBlock == NULL) {
printf("No memory to be Allocated\n");
return (*head);
}
newBlock->title=(char*)malloc(strlen(title) + 1);
if (newBlock->title == NULL) {
printf("No memory can be allocated for title\n");
return (*head);
}
newBlock->artist = (char*)malloc(strlen(artist) + 1);
if (newBlock->rating == NULL) {
printf("No memory can be alocated for artist\n");
return (*head);
}
newBlock->rating = (int)malloc(sizeof(int));
if (newBlock->rating == NULL) {
printf("No memory can be alllocated for rating \n");
return (*head);
}
strcpy(newBlock->title, title);
strcpy(newBlock->artist, artist);
newBlock->rating = rating;
newBlock->next = (*head);
newBlock->prev = NULL;
if ((*head) != NULL)
(*head)->prev = newBlock;
(*head) = newBlock;
return (*head);
}
void printSongInfo(SongNode* head) {
SongNode* ptr;
ptr = head;
printf("\n");
printf("%-35s %-35s %-35s\n", "Title", "Artist","Rating");
while (ptr != NULL) {
printf("%-35s %-35s %-35d\n", ptr->title, ptr->artist);
ptr = ptr->next;
}
}
You have a break statement in the while (rating = getnum()) loop that shouldn't be there. It will cause the loop to terminate before getting any song info.
Other problems:
newBlock->rating = (int)malloc(sizeof(int)); will leak memory, because you allocate some but never free it. Because rating is an int, memory does not need to be allocated for it. (Also, see Do I cast the result of malloc?).
getSongInfo should either return the new head, or update the current one, but not both.
Updating tail is similar to updating head, but does not need to be done all the time when inserting at the head (there's a condition that you'll need to check for).
You don't need #pragma once in a C source file. That should be in a header (and is only supported on some compilers).
Related
You should be able to enter any movie title and genre into this program as long as they are smaller then 30 characters. Then insert a rating between 1-5. The insert function takes those three inputs and stores them into a doubly linked list. After Two entries into this list the program creates an unexpected breakpoint on this line newBlock = (movieInfo*)malloc(sizeof(movieInfo)); The problems is in the insert function.
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
typedef struct movieInfo {
char* title;
char* genre;
int rating;
struct movieInfo* prev;
struct movieInfo* next;
}movieInfo;
//prototypes
int getNum(void);
movieInfo insert(char* title, char *genre,int rating, movieInfo** head, movienfo** tail);
void showList(movieInfo* head);
void eliminateEndOfLine(char* buffer);
void eliminateEndOfLine(char* buffer);
#define kWhileLoopCounter 1
#define kBuffer 30
int main() {
char title[kBuffer];
char genre[kBuffer];
int rating = 0;
movieInfo* head = NULL;
movieInfo* tail = NULL;
movieInfo* node = NULL;
while (kWhileLoopCounter) {
printf("Please enter a title or exit the loop by entering ""."" and enter \n");
fgets(title, kBuffer, stdin);
if (strcmp(title, ".\n") == 0) {
break;
}
eliminateEndOfLine(title);
printf("Please enter a genre or exit the loop by entering ""."" and enter \n");
fgets(genre, kBuffer, stdin);
if (strcmp(genre, ".\n") == 0) {
break;
}
eliminateEndOfLine(genre);
for (;;) { //jut to check and make sure the user does not enter a value greater then one
printf("Please enter a rating between 1-5\n");
rating = getNum();
if (rating < 1) {
printf("The number you have entered in less then 1,\n\n");
}
else if (rating > 5) {
printf("The number you have entered is greater than 5\n\n");
}
else {
insert(title, genre, rating, &head, &tail);
break;
}
}
}
showList(head);
freeAll(head);
return 0;}
This function seems to be the problem!!!
movieInfo insert(char title[],char genre[],int rating, movieInfo** head, movieInfo** tail) {
movieInfo* newBlock = NULL;
movieInfo* beforeElement = NULL;
movieInfo* afterElement = NULL;
newBlock = (movieInfo*)malloc(sizeof(movieInfo));
newBlock->title = (char*)malloc((strlen(title) + 1));
newBlock->genre = (char*)malloc((strlen(genre) + 1));
/*newBlock->rating = (int*)malloc(1);*/
if (newBlock == NULL) {
printf("No memory was allocated \n");
return **head;
}
strncpy(newBlock->title, title,30);
strncpy(newBlock->genre, genre,30);
newBlock->prev = newBlock->next = NULL;
if (*head == NULL) {
*head = *tail = newBlock;
return**head;
}
else if (strcmp((*head)->title, title) >= 0) {
newBlock->next = *head;
(*head)->prev = newBlock;
*head = newBlock;
}
else {
beforeElement = *head;
afterElement = (*head)->next;
while (afterElement != NULL) {
if (strcmp(afterElement->title, title) >= 0) {
break;
}
beforeElement = afterElement;
afterElement = afterElement->next;
}
newBlock->prev = beforeElement;
newBlock->next = afterElement;
beforeElement->next = newBlock;
if (afterElement == NULL)
{
*tail = newBlock;
}
else
{
afterElement->prev = newBlock;
}
}
return **head;
}
This contains the smaller functions.
void showList(movieInfo* head)
{
movieInfo* item = NULL;
item = head;
char titleHeader[] = "Title";
char genreHeader[] = "Genre";
char ratingHeader[] = "Rating";
printf("\n\n%-30s %-30s %-30s\n",titleHeader,genreHeader,ratingHeader);
while (item != NULL)
{
printf("%-30s %-30s %-30d\n", item->title,item->genre, item->rating);
item = item->next;
}
}
int getNum(void)
{/* the array is 121 bytes in size; we'll see in a later lecture how we can improve this code */
char record[121] = { 0 }; /* record stores the string */
int number = 0;
/* NOTE to student: indent and brace this function consistent with your others */
/* use fgets() to get a string from the keyboard */
fgets(record, 121, stdin);
/* extract the number from the string; sscanf() returns a number
* corresponding with the number of items it found in the string */
if (sscanf(record, "%d", &number) != 1)
{
/* if the user did not enter a number recognizable by
* the system, set number to -1 */
number = -1;
}
return number;
}
void eliminateEndOfLine(char* buffer)
{
char* target = strchr(buffer, '\n');
if (target != NULL)
{
*target = '\0';
}
}
void freeAll(movieInfo* head)//This function frees the head
{
movieInfo* curr = NULL, * next = NULL;
curr = head;
// traverse the list, being careful to not access freed blocks
while (curr != NULL)
{
// keep a pointer to the next block so we can go there after it's freed
next = curr->next;
free(curr);
curr = next;
}
}
Read carefully what strncpy does, e.g. at https://en.cppreference.com/w/c/string/byte/strncpy.
If, after copying the terminating null character from src, count is not reached, additional null characters are written to dest until the total of count characters have been written.
So if for instance title is 12 characters long, then 13 bytes will be allocated for newBlock->title, but strncpy(newBlock->title, title, 30) will write a full 30 bytes into that 13-byte array, thus overflowing it.
There is a separate bug if title is more than 30 characters long. Say it's 50. In this case you have allocated a full 51 bytes, but strncpy will only copy 30, and moreover:
If count is reached before the entire array src was copied, the resulting character array is not null-terminated.
As such, later attempts to call strcmp on the created string are likely to crash and/or give incorrect results.
Since you have allocated enough space to hold the entire string, there is no reason to cut it off at 30 characters. It is safe to just do strcpy(newBlock->title, title); which will fix both bugs and avoid unnecessary truncation to boot. If you really want to limit the title to 30 characters, then check its length when it is first input.
This recent answer has some good advice on using strncpy, i.e. "don't". Avoiding buffer overflows is important but strncpy isn't a very good solution.
One more bug is that you've forgotten to fill in newBlock->rating.
I have created a program to generate the result of a multiple choice exam. The program was supposed to show the total number of mistakes, blank answers and the number of the question which were answered incorrectly. For the following input:
6
1..223
(Here . means blank answer)
123124
The output was supposed to be:
Your result:
Mistakes: 3
Blanks: 2
Your mistakes are following:
4 5 6
Your blanks are following:
2 3
But the code shows undefined behavior. It seems to go through infinite loop. Expecting solution to my problem shortly. Thanks in advance.
#include <stdio.h>
#include <stdlib.h>
typedef struct node
{
char data;
struct node* next;
}node;
void printNode(node* head)
{
node* local = head;
int i = 0;
if(local -> data == 0)
{
printf("0");
return;
}
while(local != NULL)
{
if(i == 3)
{
i = 0;
printf("\n");
}
printf("%d\t", local -> data);
local = local -> next;
++i;
}
}
void freeNode(node** head)
{
node* temp = (*head);
while((*head) != NULL)
{
(*head) = (*head) -> next;
free(temp);
temp = (*head);
}
}
int main()
{
int n, i, flagB, flagM, blnk, mstk;
blnk = mstk = flagB = flagM = 0;
printf("Enter the number of questions: ");
scanf("%d", &n);
char ques[n], ans[n];
if(n == 0)
return 0;
node* headM = (node*)malloc(sizeof(node));
node* nodeM;
node* headB = (node*)malloc(sizeof(node));
node* nodeB;
printf("Enter your given answers: ");
fflush(stdin);
for(i = 0; i < n; ++i)
{
scanf("%c", &ques[i]);
}
fflush(stdin);
ques[n] = '\0';
printf("Enter the solution: ");
for(i = 0; i < n; ++i)
{
scanf("%c", &ans[i]);
}
ans[n] = '\0';
for(i = 0; i < n; ++i)
{
if(ques[i] == '.')
{
++blnk;
if(flagB == 0)
{
headB -> data = i + 1;
headB -> next = NULL;
nodeB = headB;
continue;
}
nodeB -> next = (node*)malloc(sizeof(node));
nodeB = nodeB -> next;
nodeB -> data = i + 1;
nodeB-> next = NULL;
flagB = 1;
}
else if(ques[i] != ans[i])
{
++mstk;
if(flagM == 0)
{
headM -> data = i + 1;
headM -> next = NULL;
nodeM = headM;
continue;
}
nodeM -> next = (node*)malloc(sizeof(node));
nodeM = nodeM -> next;
nodeM -> data = i;
nodeM-> next = NULL;
flagM = 1;
}
}
printf("Your result:\n\tMistakes: %d\n\tBlanks: %d\n", mstk, blnk);
printf("Your mistakes are follwing:\n");
printNode(headM);
printf("\nYour blanks are follwing:\n");
printNode(headB);
freeNode(&headM);
freeNode(&headM);
return 0;
}
Here are some additional thoughts. What makes your code very convoluted and hard to debug and keep the logic straight is you are mixing your linked-list Add function within the logic of your blanks and mistakes and using special conditions to handle adding the first node and subsequent nodes. This make things difficult to test and debug. If you need to add nodes to a linked-list, then write an add() function that you can thoroughly test and debug before putting it to use in your code.
Your VLAs ques and ans are too short to hold a string of n characters, at minimum they must be n + 1 characters long to provide storage for the nul-termining character that marks the end of the string. Ideally, you will make them at least 2-character longer to also hold the '\n' which will allow you to take input with fgets() rather than looping scanf() a character at a time -- which is just nuts.
You do not need to pass the address of the pointer to freeNode() simply pass a pointer. Sure freeNode() will receive a copy of the pointer -- but it will contain the original address -- and since you don't have to make any changes to that pointer available back to the caller, there is no need to pass the address of the pointer (there won't be any list left to worry about when you are done...)
So putting those pieces together, adding an add() function to add to your linked lists (See Linus on Understanding Pointers for why a pointer-to-pointer is used to iterate to the end), and adding a simple empty_stdin() function to remove the '\n' left in stdin from reading n with scanf() before making calls to fgets() later for ques and ans, you could do:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* simple function to empty stdin to end-of-line */
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
typedef struct node
{
int data;
struct node *next;
} node;
node *add(node **head, int v)
{
node **ppn = head, /* pointer to pointer to head */
*pn = *head, /* pointer to head */
*newn = malloc (sizeof *newn); /* allocate new node */
if (!newn) { /* validate allocation */
perror ("malloc-node");
return NULL;
}
newn->data = v; /* initialize members values */
newn->next = NULL;
while (pn) { /* iterate to end of list */
ppn = &pn->next;
pn = pn->next;
}
return *ppn = newn; /* add & return new node */
}
void printNode (node *head)
{
for (; head; head = head->next)
printf (" %d", head->data);
putchar ('\n');
}
void freeNode(node *head)
{
while (head != NULL)
{
node *victim = head;
head = head->next;
free(victim);
}
}
int main()
{
int n, i, blnk, mstk;
blnk = mstk = 0;
node *headM = NULL; /* declare pointers and initialize NULL */
node *headB = NULL;
printf ("Enter the number of questions: ");
/* you must VALIDATE every user-input */
if (scanf ("%d", &n) != 1) {
fputs ("error: invalid integer input.\n", stderr);
return 1;
}
empty_stdin(); /* remove '\n' (and any other chars from user) */
/* before calling fgets() below */
if (n == 0) /* check 0 BEFORE VLA declaration */
return 0;
char ques[2*n], ans[2*n]; /* declare question/answer VLAs, don't skimp */
printf("Enter your given answers: ");
if (!fgets(ques, sizeof ques, stdin)) /* read ques from stdin */
return 1;
ques[strcspn(ques, "\r\n")] = 0; /* trim '\n' from end of ques */
printf("Enter the solution: ");
if (!fgets(ans, sizeof ans, stdin)) /* read ans from stdin */
return 1;
ans[strcspn(ans, "\r\n")] = 0; /* ditto for ans */
for(i = 0; i < n; ++i) /* loop n times */
{
if(ques[i] == '.') /* if blank */
{
add (&headB, i + 1); /* add to list headB */
++blnk; /* increment counter */
}
else if(ques[i] != ans[i]) /* if mistake */
{
add (&headM, i + 1); /* add to list headM */
++mstk; /* increment counter */
}
}
printf ("Your result:\n\tMistakes: %d\n\tBlanks: %d\n"
"Your mistakes are following:\n", mstk, blnk);
printNode(headM);
printf("\nYour blanks are following:\n");
printNode(headB);
freeNode(headM); /* no need to pass the address of the pointer to free */
freeNode(headB); /* there won't be a list left when freeNode is done */
return 0;
}
There is a lot there, so go through it slowly.
Example Use/Output
$ ./bin/llquestions
Enter the number of questions: 6
Enter your given answers: 1..223
Enter the solution: 123124
Your result:
Mistakes: 2
Blanks: 2
Your mistakes are following:
4 6
Your blanks are following:
2 3
(note: in 1..223 and 123124, 5 is not a mistake, the 2 is in the correct position at the end)
Look things over and let me know if you have further questions.
I made some changes to this code, check this out.
#include <stdio.h>
#include <stdlib.h>
typedef struct Node node;
struct Node
{
int data;
struct Node * next;
};
void printNode(node *head)
{
node *local = head;
while (local != NULL)
{
printf("%d ", local->data);
local = local->next;
}
}
void freeNode(node **head)
{
node *temp = (*head);
while ((*head) != NULL)
{
(*head) = (*head)->next;
free(temp);
temp = (*head);
}
}
int main()
{
int n, i, flagB = 0, flagM = 0, blnk = 0, mstk = 0;
blnk = mstk = flagB = flagM = 0;
printf("Enter the number of questions: ");
scanf("%d", &n);
char ques[n], ans[n];
if (n == 0)
return 0;
node *headM = (node*) malloc(sizeof(node));
headM->data = 0;
node *nodeM = headM;
node *headB = (node*) malloc(sizeof(node));
headB->next = 0;
node *nodeB = headB;
printf("Enter your given answers: ");
for (i = 0; i < n; ++i)
{
scanf("%s", &ques[i]);
}
ques[n] = '\0';
fflush(stdin);
printf("Enter the solution: ");
for (i = 0; i < n; ++i)
{
scanf("%s", &ans[i]);
}
ans[n] = '\0';
fflush(stdin);
for (i = 0; i < n; ++i)
{
if (ques[i] == '.')
{ ++blnk;
if (flagB == 0)
{
nodeB->data = i + 1;
nodeB->next = NULL;
flagB = 1;
continue;
}
nodeB->next = (node*) malloc(sizeof(node));
nodeB = nodeB->next;
nodeB->data = i + 1;
nodeB->next = NULL;
}
else if (ques[i] != ans[i])
{ ++mstk;
if (flagM == 0)
{
nodeM->data = i + 1;
nodeM->next = NULL;
flagM = 1;
continue;
}
nodeM->next = (node*) malloc(sizeof(node));
nodeM = nodeM->next;
nodeM->data = i + 1;
nodeM->next = NULL;
//flagM = 1; //You made a mistake here
}
}
nodeM = headM;
nodeB = headB;
printf("Your result:\n\tMistakes: %d\n\tBlanks: %d\n", mstk, blnk);
printf("Your mistakes are following question numbers:\n");
if (mstk != 0)
printNode(headM);
else
printf("No Mistakes\n");
printf("\nYour blanks are following question numbers:\n");
if (blnk != 0)
printNode(headB);
else
printf("No Blanks\n");
freeNode(&headM);
freeNode(&headM);
return 0;
}
This code is reading a text file and inserting each word into the linked list.
I am new to linked lists and have been working on this for four hours now and cannot for the life of me figure this out.
So what is going on here? I have checked every way I know how to, and for the life of me cannot get the linked list to print correctly. I believe it has something to do with the push/append functions. Somehow it is overwriting everything previously in the linked list. Maybe the printlist function is overwriting everything but I don't see how it could possibly be doing that.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
// A complete working C program to demonstrate all insertion methods
// on Linked List
// A linked list node
struct Node;
void push(struct Node **head_ref, char *new_data);
void insertAfter(struct Node *prev_node, char *new_data);
void append(struct Node **head_ref, char *new_data);
void printList(struct Node *node);
int LINECOUNT(FILE *(*stream), char *filename);
struct Node {
char *data;
struct Node *next;
};
/* Given a reference (pointer to pointer) to the head of a list and
an int, inserts a new node on the front of the list. */
void push(struct Node **head_ref, char *new_data) {
/* 1. allocate node */
struct Node* new_node = (struct Node *)malloc(sizeof(struct Node));
/* 2. put in the data */
new_node->data = new_data;
printf("push data:%s ", new_data);
/* 3. Make next of new node as head */
new_node->next = (*head_ref);
/* 4. move the head to point to the new node */
(*head_ref) = new_node;
}
/* Given a reference (pointer to pointer) to the head
of a list and an int, appends a new node at the end */
void append(struct Node **head_ref, char *new_data) {
/* 1. allocate node */
struct Node* new_node = (struct Node *)malloc(sizeof(struct Node));
struct Node *last = *head_ref; /* used in step 5*/
/* 2. put in the data */
new_node->data = new_data;
printf("push data:%s ", new_data);
/* 3. This new node is going to be the last node, so make next of
it as NULL*/
new_node->next = NULL;
/* 4. If the Linked List is empty, then make the new node as head */
if (*head_ref == NULL) {
*head_ref = new_node;
return;
}
/* 5. Else traverse till the last node */
while (last->next != NULL)
last = last->next;
/* 6. Change the next of last node */
last->next = new_node;
return;
}
// This function prints contents of linked list starting from head
void printList(struct Node *node) {
while (node != NULL) {
printf(" %s ", node->data);
node = node->next;
}
}
int LINECOUNT(FILE *(*stream), char *filename) {
int size = 0;
size_t chrCount;
char *text;
if ((*stream = fopen(filename, "r")) == NULL) {
printf("LC Could not open hw8 data file.\n");
exit(0);
}
while (1) {
text = NULL;
getline(&text, &chrCount, *stream);
free(text); /*free text*/
if (feof(*stream))
break;
size++;
}
rewind(*stream);
return size;
}
/*int wordCount(FILE *(*stream), char *filename, int lineCount) {
char ch;
int wordcount = 0;
int charcount = 0;
*stream = fopen(filename, "r");
int x = 0;
int lineArr[lineCount];
for (int i = 0; i < lineCount; i++) {
lineArr[i] = 0;
}
if (*stream) {
while ((ch = getc(*stream)) != EOF) {
if (ch != ' ' && ch != '\n') {
charcount++;
}
if (ch == ' ' || ch == '\n') {
wordcount++;
lineArr[x] = lineArr[x] + 1;
}
if (ch == '\n') {
x++;
}
}
if (charcount > 0) {
wordcount++;
charcount++;
}
} else {
printf("Failed to open the file\n");
}
// rewind(*stream);
return lineArr;
}*/
int main(void) {
char *fn = "hw8data.txt";
int lineCount;
FILE *stream;
lineCount = LINECOUNT(&stream, fn);
//int lineArr[lineCount];
//int lineArr[];//lineArr[0] = 4 would say the first line has 4 words. using this data for strtok
//lineArr = wordCount(&stream, fn, lineCount);
//-------------------------------------
char ch;
int wordcount = 0;
int charcount = 0;
stream = fopen("./hw8data.txt", "r");
int x = 0;
int lineArr[lineCount];
for (int i = 0; i < lineCount; i++) {
lineArr[i] = 0;
}
if (stream) {
while ((ch = getc(stream)) != EOF) {
if (ch != ' ' && ch != '\n') {
charcount++;
}
if (ch == ' ' || ch == '\n') {
wordcount++;
lineArr[x] = lineArr[x] + 1;
}
if (ch == '\n') {
x++;
}
}
//if (charcount > 0) { wordcount++; charcount++; }
} else {
printf("Failed to open the file\n");
}
/* Start with the empty list */
struct Node *head = NULL;
rewind(stream);
char *sArr = malloc(42 * sizeof(char));
fscanf(stream, "%s ", sArr);
printf("%s ", sArr);
push(&head, sArr);
fscanf(stream, "%s ", sArr);
printf("%s ",sArr);
append(&head, sArr);
printList(head);
return 0;
}
char* sArr=malloc(42*sizeof(char));
fscanf(stream,"%s ",sArr);
printf("%s ",sArr);
push(&head,sArr);
fscanf(stream,"%s ",sArr);
printf("%s ",sArr);
append(&head,sArr);
You add the same value to the list twice, the value you got back from your one and only call to malloc. If you want two nodes to hold different values, don't add the same value twice. One ugly fix is if after push(&head,sArr) you add another sArr = malloc(42*sizeof(char));. That way, your call to append will add a different value to the list.
If you don't see this, add code to output the value of node->data as you print the list. You'll see that both nodes have pointers to the same chunk of memory, the value you got back from that call to malloc.
But it would be much more elegant if your list entries owned their contents. That would require functions like push and append to allocate their own pointers, copy the strings into them, and use those new pointers. Your code to destroy a list could call free on the data pointed to as well as the nodes.
I would suggest a completely different approach.
I would use a C99 flexible array member for storing each word. Also, because I don't want my code to be submittable as a homework answer, I'll show how to do it with wide-character input. (On basically all OSes except possibly Windows, it treats non-ASCII characters like Ö and Ø as letters, if your locale says they are.)
struct word {
struct word *next;
wchar_t data[]; /* Flexible array member */
};
I would use a helper function that reads the next word from a wide stream, skipping any non-word characters (which I assume to be alphanumeric characters, i.e. letters and digits):
struct word *wide_word(FILE *input)
{
struct word *w = NULL, *tempw;
size_t max = 0; /* No characters allocated in w yet */
size_t len = 0; /* No characters in w yet */
wint_t c;
/* NULL input is not allowed. */
if (!input) {
errno = EINVAL;
return NULL;
}
/* Also fail if the stream is already in an error state. */
if (ferror(input)) {
errno = EIO;
return NULL;
}
c = getwc(input);
/* Skip leading non-word characters. */
while (c != WEOF && !iswalnum(c))
c = getwc(input);
/* End of input? */
if (c == WEOF) {
errno = 0;
return NULL;
}
/* Append each wide word character. */
while (c != WEOF && iswalnum(c)) {
/* Need to reallocate? */
if (len >= max) {
/* Since words are usually short, let's allocate
in chunks of 64 wide characters. */
max = (len | 63) + 65;
tempw = realloc(w, sizeof (struct word) + max * sizeof (wchar_t));
if (!tempw) {
/* Out of memory. */
free(w);
errno = ENOMEM;
return NULL;
}
w = tempw;
}
/* Append. */
w->data[len++] = c;
c = getwc(input);
}
/* Although not useful for this particular case,
we normally want to keep the separator intact. */
if (c != WEOF)
ungetwc(c, input);
/* Optimize the memory allocated to the word. */
if (max != len + 1) {
max = len + 1;
tempw = realloc(w, sizeof (struct word) + max * sizeof (wchar_t));
if (!tempw) {
free(w);
errno = ENOMEM;
return NULL;
}
w = tempw;
}
/* Terminate the wide string in w. */
w->data[len] = L'\0';
/* Success! */
return w;
}
I personally prefer to prepend new nodes to the list, then reverse the entire list afterwards:
struct word *reverse_list(struct word *oldlist)
{
struct word *newlist = NULL;
struct word *w;
while (oldlist) {
w = oldlist;
oldlist = oldlist->next;
w->next = newlist;
newlist = w;
}
return newlist;
}
With the above, a program to read wide words from standard input is basically
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <locale.h>
#include <stdio.h>
#include <wchar.h>
#include <errno.h>
/* Functions listed above */
int main(void)
{
struct word *list, *node;
if (!setlocale(LC_ALL, ""))
fprintf(stderr, "Warning: Your C library does not support your current locale.\n");
if (fwide(stdin, 1) < 1)
fprintf(stderr, "Warning: Your C library does not support wide standard input.\n");
if (fwide(stdout, 1) < 1)
fprintf(stderr, "Warning: Your C library does not support wide standard output.\n");
/* Read words from standard input into reversed list. */
while (1) {
node = wide_word(stdin);
if (!node) {
if (errno) {
fprintf(stderr, "Error reading standard input: %s.\n", strerror(errno));
exit(EXIT_FAILURE);
}
/* No error, just end of input. */
break;
}
/* Prepend to list. */
node->next = list;
list = node;
}
/* Reverse the list so first word is first in list. */
list = reverse_list(list);
/* Print each word in the list to standard output, in order. */
for (node = list; node != NULL; node = node->next)
wprintf(L"%ls\n", node->data);
/* We could free() each word in 'list' here. */
return EXIT_SUCCESS;
}
I'm trying to finish one of my assignments and I have some issues. I have to make a program that uses struct to create a link list in which I have to add words. If the word is already in the linked list then I just have to update the frequency.
I already have this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct words Words;
struct words{
char *word;
int freq;
Words *next;
};
/*
Inserts a copy of newWord into the list, in lexicographical order. If newWord is already
in the list, increment the freq member of the node. The function returns a pointer to
the list.
*/
Words *addWord(Words *headPtr, char* newWord){
Words *current = headPtr;
if(headPtr == NULL)
{
current->word = newWord;
current->freq = 1;
}
else
{
while(current != NULL)
if(strcmp(headPtr->word, newWord))
{
current->freq++;
return headPtr;
}
else
{
current->word = newWord;
current->freq = 1;
}
}
return headPtr;
}
//prints the words in the list, along with the frequency of each word
void printWords(Words *headPtr){
while(headPtr != NULL)
{
printf("%s: %d", headPtr->word, headPtr->freq);
headPtr = headPtr->next;
}
}
//frees the entire list. Note: Words **headPtr since the headPtr NULL upon return
void deleteList(Words **headPtr){
Words *current = *headPtr;
Words *next;
while(current != NULL)
{
next = current->next;
free(current);
current = next;
}
*headPtr = NULL;
}
int main(){
char word[20];
Words *list = NULL;
scanf("%s", word);
while(!feof(stdin)){
list = addWord(list, word);
scanf("%s", word);
}
printWords(list);
deleteList(&list);
}
There are some problems in your code. See comments embedded into your code:
Words *addWord(Words *headPtr, char* newWord){
Words *current = (Words*) malloc(sizeof(Words)); // Don't malloc here.
// You don't know yet
// whether you need
// a new node or you
// you just need to
// update freq
if(current == NULL) // If current is NULL you have
// serious problems, i.e. you
// are out of memory.
// Did you really intended to do:
// if (headPtr == NULL)
{
current->word = newWord;
*current->next = (*headPtr);
(*headPtr) = *current; // I'm not sure what you try here
// but it seems strange
}
else
{
while(current != NULL)
if(strcmp(headPtr->word, newWord)) // This is not the way to compare
// strings. Two strings compare
// when "strcmp" returns 0.
//
// Further you don't want to
// use headPtr here.
{
current->freq++; // Use of uninitialized value
return; // Missing argument to return
}
else
{
current->word = newWord; // Use of uninitialized value
*current->next = (*headPtr); // Use of uninitialized value
(*headPtr) = *current;
}
}
// Missing return
}
Here is some code to start with:
#define WORD_SIZE 20
struct words{
char word[WORD_SIZE]; // Use a char array
int freq;
Words *next;
};
Words *addWord(Words *headPtr, char* newWord)
{
Words *current = headPtr; // Make a copy of headPtr
Words* new;
if ((current == NULL) || (strcmp(current->word, newWord) > 0))
{
// Insert in front of list
new = malloc(sizeof(Words)); // Allocate memory
if (new == NULL)
{
// oh, dear - out of memory - print an error message and exit
exit(1);
}
strncpy(new->word, newWord, WORD_SIZE); // Make sure not to overflow
// the buffer, so use strncpy
(new->word)[WORD_SIZE-1] = '\0'; // Make sure to zero terminate
new->freq = 1;
new->next = headPtr;
return new;
}
while(1)
{
int cmp = strcmp(current->word, newWord);
if(cmp == 0)
{
current->freq++;
return headPtr;
}
if(cmp < 0)
{
if ((current->next == NULL) || (strcmp(current->next->word, newWord) > 0))
{
// Add code to insert node after current
return headPtr;
}
}
else
{
// This should never happen...
printf("BAD CODE 1\n");
exit(1);
}
current = current->next;
}
}
The first time addToEnd() is called it works but the second time it crashes the program. I've been trying to debug it and found it happens when head->next happens. I'm a little confused because it's just being read, can that crash the program? If yes how can you possible itterate through the file?
It seems to work on certain values of entry but not others. If two entry's are the same and composed all of one letter it crashes. So if addToEnd(head, "aaaaaaaaa") is called then addToEnd(head, "aaaaaaaaa") is called the program crashes but if addToEnd(head, "aaaaaaaaa") then addToEnd(head, "aaaaaaaab") it is fine.
Here is 100% all of the code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
typedef struct node
{
char entry[21];
struct node* next;
} node;
void readDic();
void reverStr(char *str);
bool isInDic(char *reversed);
void addToEnd(node* head, char entry[21]);
unsigned int searchAndDestroy(node **head, char *entry);
void printList(node* head);
int main()
{
printf("Hello\n");
readDic();
printf("Goodbye!");
return EXIT_SUCCESS;
}
void readDic()
{
FILE* words;
char singleLine[21];
words = fopen("words.txt", "r");
node* head = malloc(sizeof(node));
fscanf(words, "%20s", head->entry);//need to do this initially
head->next = NULL;
printf("here 0");
printf("here 0.1");
if(words == NULL)
exit(EXIT_FAILURE);
printf("here 0.5");
while(fscanf(words, "%20s", singleLine) == 1)assigned input terms
{
printf("\nhere 0.6\n|%s|", singleLine);
addToEnd(head, singleLine);//problem here
printf("here 0.7");
reverStr(singleLine);
printf("here 1");
if(isInDic(singleLine)){
printf("here 2");
searchAndDestroy(&head, singleLine);
printf("here 3");
}
}
printf("here 4");
fclose(words);
printList(head);
printf("here 5");
}
//http://stackoverflow.com/questions/198199/how-do-you-reverse-a-string-in-place-in-c-or-c
/* PRE: str must be either NULL or a pointer to a
* (possibly empty) null-terminated string. */
void reverStr(char *str)
{
char temp, *end_ptr;
/* If str is NULL or empty, do nothing */
if(str == NULL || !(*str))
return;
end_ptr = str + strlen(str) - 1;
/* Swap the chars */
while( end_ptr > str )
{
temp = *str;
*str = *end_ptr;
*end_ptr = temp;
str++;
end_ptr--;
}
}
bool isInDic(char* reversed)
{
FILE* words;
char singleLine[21];
words = fopen("words", "r");
if(words == NULL)
exit(EXIT_FAILURE);
while(fscanf(words, "%20s", singleLine) == 1)//the length of the string has to be 1 less than declared size for the newline character
{
//printf("singline: %s reversed: %s\n", singleLine, reversed);
if(strcmp(singleLine, reversed) == 0)//strcmp returns 0 if both cstrings are equal
return true;
}
fclose(words);
return false;
}
void addToEnd(node* head, char entry[21])
{
printf("hi");
//printf("\naddress of next %p\n", (void *)head->next);
while(head->next != NULL)//just reading head->next screws it up
head = head->next;
printf("in addToEnd 2\n");
node* last = (node*)malloc(sizeof(node));
printf("in addToEnd 3\n");
head->next = last;
printf("in addToEnd 4\n");
strcpy(last->entry, entry);
printf("in addToEnd 5\n");
last->next = NULL;
printf("in addToEnd 6\n");
}
unsigned int searchAndDestroy(node **head, char *entry)
{
unsigned int count = 0;
while(*head)
{
node *del;
if(strcmp((*head)->entry, entry))
{ //this node stays
head = &(*head)->next;
continue;
}
/* this node goes
at this point head MUST point to the pointer that points at the node to be deleted
*/
del = *head;
*head = (*head)->next;
free(del);
count++;
}
return count; //number of nodes deleted
}
void printList(node* head)
{
printf("\nprinting everything\n");
if(head != NULL)
{
while(head->next != NULL)
{
printf("%s", head->entry);
head = head->next;
}
printf("%s", head->entry);
}
}
The answers are correct that head is being set to null but I don't see where?
When you create head, you don't set head->next to NULL.
One of three things:
If head is NULL this code will crash at the line you mention.
If last->entry is defined as char* you need to say 'last->entry = strdup(entry)
If the sizeof last->entry is less than 21, your strcpy will overflow it which
will result in undefined behavior.
Ok, with your recent edit, I assert that head is null or garbage when you call this function the second time.