I am required to have a list of structs of sentence nodes that point to a struct of word nodes. I am trying to print the user's input.
I have a program that runs properly when I manually give it the input (see test section of the code). It does not, however, work when I use my input1() function.
I've tried debugging it, but I can't seem to find the problem.
I removed all printf lines that I used to debug. I also removed all the irrelevant code.
I am looking to know how to fix it and what is wrong so I can run it with no problems.
What I learned from debugging it is that (only when using input1() and not in the test) the head is overwritten every time and all the nodes as well.
I also tried using a double pointer instead of returning para but that didn't help.
any help will be appreciated,
thanks in advance
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
typedef struct word
{
char * ch;//poiter to char
}
W;
typedef struct sentence
{
W * currentWord;//pointer to a word
int lineNumber;// holds the line number
int numbersOfWords;//holds the number of words
struct sentence* link;
}
sent;
typedef struct list
{
sent* head;
int count;
}
LISTS;
LISTS* createList()
{
LISTS* list;
list= (LISTS*) malloc (sizeof (LISTS));
if (list)
{
list-> head = NULL;
list-> count = 0;
}
return list;
} // createList
void printList(LISTS* list)
{
sent *temp = list -> head;
//iterate the entire linked list and print the data
while(temp != NULL)
{
printf("%s\n", temp->currentWord->ch);
temp = temp->link;
}
// printf("NULL\n");
}
void insertSentList (LISTS* list, W* itemPtr)
{
sent* newPtr; //new node
if (!(newPtr = (sent * ) malloc(sizeof(sent)))){
printf(" Memory can not be allocated.");
return;
}
newPtr->currentWord = itemPtr;
newPtr->link = NULL;
if(list->head == NULL)
{
list->head = newPtr;
}else{
sent* current = list->head;
while(current->link != NULL){
current = current->link;
}
current -> link = newPtr;
}
(list->count)++;
return;
} // insertList
LISTS * input1(LISTS *para)
{
char * line;
line = (char * ) malloc(1000 * sizeof(char));
line[0] = '\0';
while (line[0] != '\n')
{
W word;
word.ch = (char * ) malloc(100);
printf(" Please input a line : ");
fgets(line, 1000, stdin);
if(line[0] != '\n'){
strcpy(word.ch, line);
insertSentList(para,&word);
}
}
free(line);
return para;
}
int main()
{
///////////////////test////////////////
LISTS* list = createList();
W word;
word.ch= "word0 ";
W word1;
word1.ch= "word1 ";
W word2;
word2.ch= "word2";
insertSentList(list,&word);
insertSentList(list,&word1);
insertSentList(list,&word2);
insertSentList(list,&word);
insertSentList(list,&word1);
insertSentList(list,&word2);
printList(list);
///////////////////test////////////////
LISTS *para = createList();
para= input1(para);
printList(para);
return 0;
}
Main problem with the posted code is that "ownership" of the sent and W objects in a list is not well defined. For example word.ch= "word0 "; in main sets the ch pointer pointing to a string literal (which it does not own), but word.ch = malloc(100); in input1 points it to dynamically allocated memory (which it should own, and remember to free later). Because of this, memory allocations cannot be tracked reliably and, even in the cases where things appear to "work", there are multiple memory leaks. It also breaks when the inserted objects are local variables that do not live for the entire lifetime of the list object.
The simplest (if not necessarily the best or most efficient) solution would be to dynamically allocate all objects that go into the list, make the list own them all, and add a function to cleanup once done. To that end insertSentList could be modified as follows.
void insertSentList (LISTS* list, W* itemPtr)
{
sent* newPtr; //new node
if (!(newPtr = malloc(sizeof(sent)))){
printf(" Memory can not be allocated.\n");
return;
}
W *newItem = malloc(sizeof(W)); // <-- make a deep copy of the `itemPtr` argument
newItem->ch = strdup(itemPtr->ch); // including a copy of the string itself
newPtr->currentWord = newItem; // <-- save the copy in the list, not the argument
newPtr->link = NULL;
if(list->head == NULL)
{
list->head = newPtr;
}else{
sent* current = list->head;
while(current->link != NULL){
current = current->link;
}
current->link = newPtr;
}
list->count++;
} // insertList
For proper cleanup and to avoid memory leaks, the following freeList should be called for each list pointer returned by createList and filled by insertSentList.
void freeList(LISTS *list)
{
sent *temp = list->head;
while(temp != NULL)
{
sent *next = temp->link;
free(temp->currentWord->ch);
free(temp->currentWord);
free(temp);
temp = next;
}
free(list);
}
Related
I am building a program for a project. One of the requirements for the project is a function that selects a random node from my linked list of 3000 words.
I tried to do this by creating a function that generates a random number from 0 to 2999. After this, I created another function that follows a for loop starting from the head and moving to the next node (random number) times.
My random number generator is working fine, but my chooseRand() function is not.
Please help, the random number generator and the chooseRand() function are the last two functions above main. Also, my code is a bit messy, sorry.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
int nodeNum;
int chances;
char* secret;
/*Node of linked list*/
typedef struct node {
char *data;
struct node *next;
} node;
node *start = NULL;
node *current;
/*Void function to print list*/
void printList(struct node *node)
{
while (node != NULL) {
printf("%s ", node->data);
node = node->next;
}
}
/*Appending nodes to linked list*/
void add(char *line) {
node *temp = malloc(sizeof(node));
temp->data = strdup(line);
temp->next = NULL;
current = start;
if(start == NULL) {
start = temp;
} else {
while(current->next != NULL) {
current = current->next;
}
current->next = temp;
}
}
void readfile(char *filename) {
FILE *file = fopen(filename, "r");
if(file == NULL) {
exit(1);
}
char buffer[512];
while(fgets(buffer, sizeof(buffer), file) != NULL) {
add(buffer);
}
fclose(file);
}
node *listSearch(node* start, char *nodeSearched){
node *p;
for (p = start; p != NULL; p = p->next)
if (strcmp(p->data, nodeSearched) == 0)
printf("%s", p->data);
return NULL;
}
node *letterSearch(node* start, int i){
node *p;
for (p = start; p != NULL; p = p->next)
if (strlen(p->data) == i)
{
printf("\n %s", p->data);
free(p);
p = NULL;
}
return NULL;
}
void chooseRand(struct node* start)
{
node* p;
int n;
p = start;
for(n = 0; n != nodeNum; n++)
{
p = p->next;
}
printf("%s", p->data);
}
void randNum(int lower, int upper)
{
srand(time(0));
nodeNum = (rand() % (upper - lower + 1)) + lower;
}
int main(){
randNum(0, 2999);
chooseRand(start);
return 0;
}
As others has said, the problem is that you don't have initialized the linked list yet, because of what your are getting a segmentation fault. So, in addition to initializing the list first, you must also introduce checks in the implementation of the chooseRand function, to check that if you reach the end of the list, without reaching the desired index, you stop executing the foor loop, otherwise you will be potentially exposed to segmentation faults.
Improve chooseRand implementation, to prevent segmentation fault either, when the linked list is empty, or when the randomly generated nodeNum is grater than the the index of the list's last item:
void chooseRand(struct node* start)
{
node* p;
int n;
p = start;
if(p == NULL){
printf("The list is empty!");
return;
}
// Also, we must stop the iteration, if we are going to pass the end of the list, you don't want a segmentation fault because trying to access a NULL pointer:
for(n = 0; n != nodeNum && p->next != NULL; n++)
{
p = p->next;
}
// If p == NULL, the list was not big enough to grab an item in the `nodeNum` index:
printf("%s", (n != nodeNum) ? "Not found!" : p->data);
}
Initialize the linked list, with the content of some file on disk:
int main(){
randNum(0, 2999);
// Fill the linked list with the content of a file in disk, calling your method:
char fileName[] = "PutYourFileNameHere.txt";
readfile(fileName);
chooseRand(start);
return 0;
}
There is another fix that you must do, and it is free the memory being hold by the pointer field data of your structure, in the implementation of your method letterSearch. Inside the if statement, you're de-allocating the memory hold by the p pointer, but you aren't de-allocating the memory assigned to the pointer p->data, this will cause a memory leak. When you in the function add, initialized p->data with the result of the call to the function strdup(line), what this function does is allocate enough memory in the heap, copies to it the buffer pointed by the line argument, and give to you back a pointer to the new allocated memory, that you're storing in the p.data field; a pointer that you should free when you're done with it, otherwise your program will have potential memory leaks. So I will modify your function letterSearch as folollows:
node *letterSearch(node* start, int i){
node *p;
for (p = start; p != NULL; p = p->next)
if (strlen(p->data) == i)
{
printf("\n %s", p->data);
// Free p->data before free p:
free(p->data);
free(p);
p = NULL;
}
return NULL;
}
References:
strdup
Hi I was deleting memory leaks in my code but I stucked in on moment.
Here I've got functions:
char* MakeLowerCase(char* word)
{
char* lower = (char *)malloc(sizeof(char)*strlen(word)+1);
strcpy(lower, word);
int i = 0;
for(i = 0; i < strlen(lower); i++){
lower[i] = tolower(lower[i]);
}
return lower;
}
void sortedInsert(Word** pH, Word* new_node)
{
Word* current;
/* Special case for the head end */
if (*pH == NULL || strcmp(MakeLowerCase((*pH)->word), MakeLowerCase(new_node->word)) == 1)
{
new_node->pNext = *pH;
*pH = new_node;
}
else
{
/* Locate the node before the point of insertion */
current = *pH;
while (current->pNext!=NULL &&
strcmp(MakeLowerCase(current->pNext->word), MakeLowerCase(new_node->word)) == -1)
{
current = current->pNext;
}
new_node->pNext = current->pNext;
current->pNext = new_node;
}
}
After using these functions my whole list was sorted. But to avoid memory leaks from MakeLowerCase I tried to so something like this:
void sortedInsert(Word** pH, Word* new_node)
{
Word* current;
/* Special case for the head end */
if(*pH = NULL)
{
*pH = new_node;
return ;
}
char* word1 = MakeLowerCase((*pH)->word);
char* word2 = MakeLowerCase(new_node->word);
if (*pH == NULL || strcmp(word1, word2) == 1)
{
new_node->pNext = *pH;
*pH = new_node;
}
else
{
/* Locate the node before the point of insertion */
current = *pH;
char* word3 = MakeLowerCase(current->pNext->word);
char* word4 = MakeLowerCase(new_node->word);
while (current->pNext!=NULL && strcmp(word3, word4) == -1)
{
current = current->pNext;
}
new_node->pNext = current->pNext;
current->pNext = new_node;
}
free(word1);
free(word2);
}
After changes my list isn't sorted as earlier (just a part of it is sorted in weird way). What did I wrong?
As you already noticed, the original approach leaks memory. The returned memory from MakeLowerCase() was never freed. In your second attempt you tried to fix this but leaked now word3 and word4. You fixed that later.
The main error is that in your revised function you have a loop advancing current while comparing word3 against the new item. You missed to update word3 inside the loop after advancing current. And You have to free it too.
As mentioned in my comment you should simply use stricmp(), the ignore-case brother of strcmp, so you don't need MakeLowerCase() with all the leaks.
Here my short code. With linked lists I love to use double indirection like Word** because it reduces the number of edge cases and by thus the number of conditional instructions.
void sortedInsert(Word** pHead, Word* new_node)
{
//*pHead is a possible insertion point for new_node, like *pHead=new_node
while (*pHead)
{
if (stricmp(new_node->word, (*pHead)->word) < 0)
break; //Insertion point found.
pHead = &(*pHead)->pNext;
}
//*pHead is the insertion point for new_node, the remaining list at *pHead goes behind us
new_node->pNext = *pHead;
*pHead = new_node;
}
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;
}
}
I tried reading from text file, and then put every word in list node(and print it afterwards in reverse order).
The program works good, but when trying to free the allocated list nodes, the program crash.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <assert.h>
#include <math.h>
typedef struct node{
char* word;
struct node* next;
}; typedef struct node* list;
void freeall(list lst){
list temp = NULL;
while (lst)
{
temp = lst->next;
free(lst);
lst = temp;
}
#if 0
if (lst == NULL){ return; }
freeall(lst->next);
free(lst->word);
free(lst);
#endif // 0
}
void deleteAllNodes(list start)
{
while (start != NULL)
{
list temp = start;
free(temp);
start = start->next;
}
}
list createNode(char* buff){
list newnode = (list)malloc(sizeof(list));
assert(newnode);
newnode->next = NULL;
newnode->word = (char*)calloc(strlen(buff), sizeof(char));
assert(newnode->word);
strcpy(newnode->word, buff);
return newnode;
}
void reverse(const char *str) //you don't need to modify your string
{
if (*str != '\0'){ //if the first character is not '\O'
reverse((str + 1)); // call again the function but with +1 in the pointer addr
printf("%c", *str); // then print the character
}
}
void print_reverse(list lst){
if (lst == NULL) return;
print_reverse(lst->next);
reverse(lst->word);
//free(lst->word);
}
list createList(FILE* ifp){
struct node *loop = NULL;
list curr = NULL;
list lst = NULL;
char *word = NULL;
size_t size = 2;
long fpos = 0;
char format[32];
if (ifp == NULL) // open file
perror("Failed to open file \n");
if ((word = malloc(size)) == NULL) // word memory
perror("Failed to allocate memory");
sprintf(format, "%%%us", (unsigned)size - 1); // format for fscanf
while (fscanf(ifp, format, word) == 1) {
while (strlen(word) >= size - 1) { // is buffer full?
size *= 2; // double buff size
printf("** doubling to %u **\n", (unsigned)size);
if ((word = realloc(word, size)) == NULL)
perror("Failed to reallocate memory");
sprintf(format, "%%%us", (unsigned)size - 1);// new format spec
fseek(ifp, fpos, SEEK_SET); // re-read the line
if (fscanf(ifp, format, word) == 0)
perror("Failed to re-read file");
}
curr = createNode(word);
if (lst == NULL){lst = curr;}
else{
loop = lst;
while (loop->next != NULL) {//loop to last structure
loop = loop->next;//add structure to end
}
loop->next = curr;
}
fpos = ftell(ifp); // mark file pos
}
free(word);
return lst;
}
int main(int argc, char* argv[]){
assert(argc == 2);
FILE *ifp = fopen(argv[1], "r");
assert(ifp);
list lst = NULL;
lst = (list)malloc(sizeof(list));
lst = createList(ifp);
print_reverse(lst);
fclose(ifp);
//freeall(lst);
//deleteAllNodes(lst);
return 1;
}
your delete all nodes has a bug in it. You freed a pointer and tried accessing it immediately. So the program crashes You can try this
void deleteAllNodes(list head)
{
list ptr = head;
while ((ptr = head) != NULL)
{
head = head->next;
free (ptr);
}
}
point the current ptr to the head and point head to next element. Delete the current pointer.
In your deleteAllNodes function you are free-ing a pointer and then accessing it. You could try deleting nodes in reverse order, starting from the last one, for instance with a recursive function.
void deleteAllNodes(list start)
{
if (start != NULL)
{
deleteAllNodes(start->next);
free(start);
}
}
Or you can stick to the iterative forward deletion with something like (untested):
void deleteAllNodes(list start)
{
list previous = NULL;
while (start != NULL)
{
if (previous != NULL)
free(previous);
previous = start;
start = start->next;
}
if (previous != NULL)
free(previous);
}
The problem , as I see it is with
list newnode = (list)malloc(sizeof(list));
your list is a typedef to struct node*, so this statement is essentially
list newnode = (list)malloc(sizeof(struct node*));
which is wrong. You're allocating memory for a pointer to structure variable, whereas, you should be allocating memory equal to the size of the structure variable itself.
Two things to mention here
Please see why not to cast the return value of malloc() and family in C.
Never use a typedef for a pointer type. It's not a "rule", but better to stick to it.
Your allocation statement, at least, shall look like
list = malloc(sizeof*list);
Apart from this, in your main() function,
First, you're allocating memory to lst using malloc() [Same issue with the allocation as above]
Then, you assign another pointer, the return value of createList() to lst.
This way, you're overwriting the allocated mekory through malloc(), creating memory leak. You don't need malloc() there, at all.
I have created a linked list in C that is used to store data which is then modified as required. In creating the linked list I have used the following
struct car_elements
{
char car_rego[7];
double time_parked;
struct car_elements *next;
};
typedef struct car_elements car;
/* Defined as global variable to hold linked list */
car *head = NULL;
car *SetupCars()
{
car *ptr = head;
car *new_car = NULL;
new_car = (car*) malloc(sizeof(car));
if (!new_car)
{
printf("\nUnable to allocate memory!\n");
exit(1);
}
strcpy(new_car->car_rego, "empty");
new_car->time_parked = time(NULL);
new_car->next = NULL;
if (ptr == NULL)
{
return (new_car);
}
else
{
while (ptr->next)
{
ptr = ptr->next;
}
ptr->next = new_car;
return (head);
}
}
From main I call the following to create the linked list
for(int i = 0; i<TOTAL_CARS; i++) {
head = SetupCars(head);
}
The problem is that now I have a memory leak - Is there a better way to create a fixed size linked list. At the end of the program running I can
free(head);
However I cannot call within SetupCars method
free(new_car);
I could create new_car as a global variable I guess and free it at the end of the program but I cannot help but feel there is a better way to do it. I don't think global variables are evil if used properly however I would appreciate some advice.
WHy not just free it at the end? SOmething like this:
car *tofree;
car *ptr = head;
while(ptr) {
tofree = ptr;
ptr = ptr->next;
free(tofree);
}
You need a function to free the entire list, like:
void free_cars(car*p) {
while (p != NULL) {
car* nextp = p->next;
free (p);
p = nextp;
}
}
So you would call
free_cars (head);
head = NULL;
Perhaps even by having a macro
#define DELETE_CARS(CarsVar) do { \
free_cars(CarsVar); CarsVar = NULL; } while(0)
then just write DELETE_CARS(head); later in your code.
And indeed, manual memory management is painful, you need to avoid memory leaks. Tools like valgrind can be helpful. And you could consider instead to use Boehm's garbage collector, so use GC_MALLOC instead of malloc and don't bother freeing memory.... Read more about garbage collection.
Keep car *head as a global var. For SetupCars:
void SetupCars() /* void will do, unless you want a copy of the new "car" */
{
car *new_car = NULL;
new_car = malloc(sizeof *new_car); /* don't need to cast return value of malloc */
/* do checks and setup new_car... */
if (head == NULL) /* first element */
{
head = new_car;
}
else /* easier to add new_car as the FIRST element instead of last */
{
new_car->next = head;
head = new_car;
}
}
From main you create the linked list the same way:
for(int i = 0; i<TOTAL_CARS; i++) {
SetupCars(); /* without any arguments */
}
Then at the end, you loop through the list and free the objects. As Manoj Pandey posted in his answer:
car *tofree;
car *ptr = head;
while(ptr) {
tofree = ptr;
ptr = ptr->next;
free(tofree);
}