I have to create a quicksort on a linked list (in C).
I have my first and last pointer an the pivot (in this code it's the first element of the list).
The structs I have to use:
typedef struct list_element list_element;
struct list_element {
char *password;
int count;
list_element* next;
};
typedef struct list list;
struct list {
list_element* first;
list_element* last;
};
I have a file with 100 passwords and counts.
Like this:
password1 123 (next line) password2 435 (next line) password3 133 ...
The passwords have to be sorted (according to their count) in the end of this programm.
There isn't needed any extra memory allociation for the left and right lists because I only have to use the next pointers. (That's what the hint in the exercise says.)
The given main-function:
int main(int argc, char** args)
{
if (argc != 2)
{
printf("Nutzung: %s <Dateiname>\n",args[0]);
return 1;
}
list mylist;
init_list(&mylist);
read_data(args[1],&mylist);
qsort_list(&mylist);
printf("Sortierte Liste:\n");
print_list(&mylist);
free_list(&mylist);
return 0;
}
I have initialized my list:
void init_list(list* mylist)
{
mylist->first = NULL;
mylist->last = NULL;
}
And insert a new element at end (passwort = passwords in file, hauefigkeit = counts in file):
void insert_list(list_element* le, list* mylist)
{
if (mylist->first != NULL) {
le->next = mylist->last;
mylist->last = le;
le->next= NULL;
}
else {
mylist->last->next = le;
mylist->last = le;
mylist->last->next = NULL;
}
}
Read data from file:
void read_data(char* filename, list* mylist)
{
FILE *file_in = fopen(filename, "r");
if (file_in == NULL) {
perror("Could not open input file!");
exit(1);
}
char buffer[999] = "0";
char *passwort = (char*) calloc(1,sizeof(passwort));
int haeufigkeit = 0;
while (fgets(buffer, sizeof(buffer), file_in) != NULL) {
sscanf(buffer, "%s %d", passwort, &haeufigkeit);
list_element* le = (list_element*)calloc(1,sizeof(list_element));
for(int i = 0; i <=100; i++) {
le->password[i] = passwort[i];
}
le->count = haeufigkeit;
le->next = NULL;
insert_list(le, mylist);
}
fclose(file_in);
}
Partition of the list:
list_element* partition( list* input, list* left, list* right )
{
list_element* pivot = NULL;
if (input->first != NULL) {
list_element* temp;
pivot = input->first;
input->first = input->first->next;
pivot->next = NULL;
left->first = NULL;
right->first = NULL;
while (input->first != NULL) {
if((pivot->count)>(input->first->count)){
temp=input->first->next;
insert_list(input->first, left);
input->first=temp;
}
else {
temp = input->first->next;
insert_list(input->first, right);
input->first = temp;
}
}
}
return pivot;
}
The actual quicksort:
void qsort_list(list* mylist)
{
if(mylist->first == mylist->last){
}
else{
list* left = calloc(1,sizeof(list));
list* right= calloc(1,sizeof(list));
list_element* pivot = partition(mylist, left, right);
qsort_list(left);
qsort_list(right);
if(left->first == NULL){
mylist->first = pivot;
}
else{
mylist->first = left->first;
left->last->next = pivot;
}
if(right->first == NULL){
pivot->next = NULL;
mylist->last = pivot;
}
else{
pivot->next = right->first;
mylist->last = right->last;
}
free(right);
free(left);
}
}
In the end print list:
void print_list(list* mylist)
{
list_element *elem = mylist->first;
while (elem != NULL) {
printf("%s %d\n", elem->password, elem->count);
elem = elem->next;
}
}
And free list:
void free_list(list* mylist)
{
list_element *current;
list_element *second;
current = mylist->first;
while (current != NULL) {
second = current->next;
free(current);
current = second;
}
}
Syntax should be ok. GCC (c99, Wall) compiles without any problems.
But there is an segmentation fault. I have been searching for hours now and I have no idea where the problem could be. Maybe you can help me with this problem.
After the first two answers there isn't any segmentation fault. But still have a problem with read_data function.
The program can't read the passworts correctly. Maybe i misunderstood you answers in relation to the read function.
That's the current function:
void read_data(char* filename, list* mylist)
{
FILE *file_in = fopen(filename, "r");
if (file_in == NULL) {
perror("Could not open input file!");
exit(1);
}
char buffer[999] = "0";
int haeufigkeit = 0;
while (fgets(buffer, sizeof(buffer), file_in) != NULL) {
char passwort[100];
sscanf(buffer, "%s %d", passwort, &haeufigkeit);
list_element* le = (list_element*)
calloc(1,sizeof(list_element));
le->password = passwort;
le->count = haeufigkeit;
le->next = NULL;
insert_list(le, mylist);
}
fclose(file_in);
}
As Leonardo Alves Machado pointed out, the first reflex when having trouble with a C/C++ program is to run it with a debugger like gdb. Here is the basics:
gcc -g main.c -o main
gdb main
(gdb) run
Note the -g compilation flag: this will add debug information to the executable.
In read_data, the lines
for(int i = 0; i <=100; i++) {
le->password[i] = passwort[i];
}
really bug me. You allocate space for passwort (which you never free by the way) and try to copy it to le->password, which is a simple pointer (no allocated space). What you actually need is to make le->password point to passwort, i.e.
le->password = passwort;
In free_list, don't forget to deallocate the passwort space before deallocating the list_element space with:
while (current != NULL) {
second = current->next;
free(current->password);
free(current);
current = second;
}
One of the first issues your program encounters is that read_data() does not allocate enough space for passwort. It's unclear, actually, why you are dynamically allocating this at all, but given that you are doing so, sizeof(passwort) is the size of one pointer to char (since that's what passwort is) -- probably either 4 or 8 bytes. Later, you seem to assume that the allocated space is 100 bytes long when you (attempt to) copy its contents into a list element. Why not simply declare it as a 100-char array?
char passwort[100];
Indeed, if you also declare list_element.passwort the same way then your password-copying code inside the loop will be correct, albeit a bit non-idiomatic.
As it is, that code is problematic, as #Derlin observes. His proposed solution is incorrect, however; you must not make the list elements point to the local passwort as long as that is allocated only once for the whole routine. Then all list elements will have the same password string, which is not what you want. If you want your list elements to contain pointers to the passwords, as they do now, then you'll want to move the declaration and allocation of passwort inside the loop, so that you get separate password space allocated for each list element. Then the suggestion to assign le->password = passwort would be correct.
Another early issue is that your insert_list() function is badly broken.
Consider first what happens when you try to insert an element into an empty list, as initialized by init_list(). The list's next and last members will both be null, and insert_list() will therefore attempt to execute this code:
mylist->last->next = le;
mylist->last = le;
mylist->last->next = NULL;
Observe that mylist->last is null, therefore the first line invokes undefined behavior by attempting to dereference a null pointer. A segmentation fault is an eminently plausible observed result. You might fix that by changing the first of those lines to
mylist->next = le;
Now consider what happens when you try to insert into a non-empty list. In that case, you execute these lines:
le->next = mylist->last;
mylist->last = le;
le->next= NULL;
Since your intention is to insert the new element at the end (i.e. to append it), it is odd that you set the new element's next pointer to the list's last element. It is especially odd that you later overwrite that value with NULL. You seem to have it backward: you want to set the initial last element to point to the new element as its next element, not the other way around:
mylist->last->next = le;
Indeed, that's exactly the code that was wrong for the empty-list case, but it's fine when the list is non-empty.
Overall, your function also suffers from an odd lack of parallelism and some hidden code duplication. I'd probably write the overall function something more like this:
void append_to_list(list_element* le, list* mylist)
{
le->next= NULL;
if (mylist->first != NULL) {
mylist->last->next = le;
mylist->last = le;
}
else {
mylist->first = le;
mylist->last = le;
}
}
Related
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);
}
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 have a linked list of "words" that I'm trying to build, I made a function called "add_to_mem" which adds to the linked list the next word.
I've made a couple of checks on the code, and found out that he works twice - once when the linked list is a NULL, and once when it's not - and it is does working, but in the third time I'm calling to the method - I'm getting an "A heap has been corrupted" error.
The code:
typedef struct { unsigned int val : 14; } word;
typedef struct machine_m
{
word * current;
int line_in_memo;
char * sign_name;
struct machine_m * next_line;
}Machine_Memo;
The function:
/*Adding a word to the memory.*/
void add_to_mem(word * wrd, int line, char * sign_name)
{
Machine_Memo * temp = NULL, *next = NULL;
if (machine_code == NULL)
{
machine_code = (Machine_Memo *)malloc(sizeof(Machine_Memo));
if (machine_code == NULL)
{
printf("Memory allocation has failed.");
exit(1);
}
machine_code->current = wrd;
machine_code->line_in_memo = line;
machine_code->sign_name = sign_name;
machine_code->next_line = NULL;
}
else
{
printf("token has been reached");
temp = machine_code;
next = (Machine_Memo *)malloc(sizeof(Machine_Memo)); //Line of error
if (next == NULL)
{
printf("MEMORY ALLOCATION HAS FAILED. EXITING PROGRAM.\nThe problem has occured on code line %d", 775);
exit(0);
}
next->current = wrd;
next->line_in_memo = line;
next->sign_name = sign_name;
next->next_line = NULL;
while (temp->next_line != NULL)
{
temp = temp->next_line;
temp->next_line = next;
}
}
}
As far as I understand the code, it does not create a linked list. it creates nodes, but does not link them together.
at first call, the machine_code (head of list) is created.
at next call, the node 'next' is created, however, the loop:
while (temp->next_line != NULL)
{
temp = temp->next_line;
temp->next_line = next;
}
does nothing as the 'machine_code->next' value is null. so code inside the loop is not executed. and we do not get here a linked list, but sporadic nodes not connected each to other.
you may wanted (as pointed at the other post here) to have something like:
while (temp->next_line != NULL)
{
temp = temp->next_line;
}
temp->next_line = next;
Here
while (temp->next_line != NULL)
{
temp = temp->next_line;
temp->next_line = next; // move out of the loop
}
you may want to move the last assignment outside of the loop.
I'm suppose to implement QuickSort on a linked list, but I'm having trouble reading from text file into the list and printing all the nodes. I only get the last element printed as Output. What am I doing wrong?
My text file looks like this (password and its usage frequency):
asdfgh 31554
snoopy1 15637
qwertyuiop 24372
soccer 21208
.
.
Here's my structs
struct list_element {
char *password;
int count;
list_element* next;
};
struct list {
list_element* first;
list_element* last;
};
ReadfromData()
void read_data(char* filename, list* mylist)
{
FILE *fp;
char password[128];
int freq;
fp = fopen(filename, "r");
if(fp == NULL)
{
perror("Error opening file");
return;
}
while(fgets(password, sizeof(password), fp))
{
list_element *node = malloc(sizeof(list_element));
char *token;
token = strtok(password, " ");
node->password = strdup(token);
if( token != NULL ){
token = strtok(NULL, " ");
}
freq = atoi(token);
node->count = freq;
node->next = NULL;
insert_list(node, mylist);
}
fclose(fp);
}
Insert infront in List
void insert_list(list_element* le, list* mylist)
if((mylist->first = NULL)){
mylist->first = le;
}else{
le->next = mylist->first;
mylist->first = le;
}
Print list
void print_list(list* mylist)
list_element *temp;
temp = mylist->first;
while(temp != NULL)
{
printf("pass %s and count %d \n", temp->password, temp->count);
temp = temp->next;
}
I've also wrote a small function, which I call in the beginning of the program to intailize the list:
void init_list(list* mylist){
mylist = (list*)malloc(sizeof(list));
mylist->first = mylist->last = NULL;
}
but I don't think it makes sense to do a malloc here too, since I already create node one by one, right? Confused abit.
Any advice would be great!
Lets take a closer look at the init_list function:
void init_list(list* mylist){
mylist = (list*)malloc(sizeof(list));
mylist->first = mylist->last = NULL;
}
The argument mylist is a local variable. And as such it will go out of scope when the function ends and all changes to it will be lost. Because of this the pointer you will be using after the call to init_list will not actually be initialized and you will have undefined behavior when you use it.
There are two solutions: Either have init_list take no argument and instead return the new list. Or you emulate pass by value by passing a pointer to the list variable from the calling function, meaning the init_list function takes a pointer to a pointer to the structure.
The second alternative could look like this
void init_list(list **mylist)
{
*mylist = malloc(sizeof **mylist);
(*mylist)->first = (*mylist)->last = NULL;
}
Then you call it using the address-of operator:
list *mylist;
init_list(&mylist);
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.