I'm working on pset6 of Harvard's cs50 now, the problem is to implement a trie dictionary.
I finally managed to make it work with a small problem.
When I run valgrind to check memory leak, it tells me that I've freed one more than I've allocated, but I can't see any problem in my unload function.
It also warns me that there are some uninitialized values, but I can't figure it out although it won't affect the result.
Here is my entire code:
/****************************************************************************
* dictionary.c
*
* Computer Science 50
* Problem Set 6
*
* valgrind warn that there are uninitialized values, could be the node struct, but don't
* know how to initialize it, anyway, it works at last!
*
* Implements a dictionary's functionality.
***************************************************************************/
#include <stdbool.h>
#include <ctype.h>
#include "dictionary.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define HASHTABLE_SIZE 5000
int count = 0; // gloabal counter
typedef struct node { // data structure
bool end_word;
struct node *children[27];
} node;
int
charNumber(char c); // function prototype
void
freeNode(node *currentNode);
node root = {false,{NULL}};
/*
* Returns true if word is in dictionary else false.
*/
bool
check(const char *word)
{
node *ptr = &root;
for (int i=0;i<strlen(word);i++)
{
if (ptr->children[charNumber(word[i])] == NULL)
return false;
ptr = ptr->children[charNumber(word[i])];
}
if (ptr->end_word)
return true;
else
return false;
}
/*
* Loads dictionary into memory. Returns true if successful else false.
*/
bool
load(const char *dictionary)
{
// char word[LENGTH+1]; // must initialize to zero! Or there will be some weird problem.
FILE *fp = fopen(dictionary,"r");
if (fp == NULL)
return false;
while (!feof(fp))
{
char word[LENGTH+1] = {};
fscanf(fp,"%s\n",word); // have to use "%s\n" instead of "%s", or the count will be wrong, don't know why.
count++;
node *ptr = &root;
for (int i=0;i<strlen(word);i++)
{
if (ptr->children[charNumber(word[i])] == NULL)
{
node *new = malloc(sizeof(node));
*new = (node) {false,{NULL}}; // initiallization
ptr->children[charNumber(word[i])] = new;
ptr = new;
}
else
{
ptr = ptr->children[charNumber(word[i])];
}
}
ptr->end_word = true;
}
fclose(fp);
return true;
}
/*
* caculate a number for the character
*/
int
charNumber(char c)
{
int num;
if (c == '\'')
return 26;
else if(c >= 'A' && c <= 'Z')
c += 32;
num = c - 'a';
return num;
}
/*
* Returns number of words in dictionary if loaded else 0 if not yet loaded.
*/
unsigned int
size(void)
{
if (count)
return count;
else
return 0;
}
/*
* Unloads dictionary from memory. Returns true if successful else false.
*/
bool
unload(void)
{
freeNode(&root);
return true; // can't figure out when to return false...
}
void freeNode(node *currentNode)
{
for (int i=0;i<27;i++)
{
if (currentNode->children[i] != NULL)
freeNode(currentNode->children[i]);
}
free(currentNode);
}
Here is some of the valgrind output:
==22110== Invalid free() / delete / delete[]
==22110== at 0x4024ECD: free (vg_replace_malloc.c:366)
==22110== by 0x8048F90: freeNode (dictionary_tries.c:152)
==22110== by 0x8048F45: unload (dictionary_tries.c:141)
==22110== by 0x8048AB5: main (speller.c:158)
==22110== Address 0x804a5a0 is 0 bytes inside data symbol "root"
==22110==
--22110-- REDIR: 0x40b2930 (strchrnul) redirected to 0x4028570 (strchrnul)
==22110==
==22110== HEAP SUMMARY:
==22110== in use at exit: 0 bytes in 0 blocks
==22110== total heap usage: 367,083 allocs, 367,084 frees, 41,113,776 bytes allocated
==22110==
==22110== All heap blocks were freed -- no leaks are possible
==22110==
==22110== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 14 from 9)
==22110==
==22110== 1 errors in context 1 of 1:
==22110== Invalid free() / delete / delete[]
==22110== at 0x4024ECD: free (vg_replace_malloc.c:366)
==22110== by 0x8048F90: freeNode (dictionary_tries.c:152)
==22110== by 0x8048F45: unload (dictionary_tries.c:141)
==22110== by 0x8048AB5: main (speller.c:158)
==22110== Address 0x804a5a0 is 0 bytes inside data symbol "root"
==22110==
--22110--
--22110-- used_suppression: 14 U1004-ARM-_dl_relocate_object
==22110==
==22110== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 14 from 9)
Suppose your load function opens a blank file. feof(fp) will initially return 0, because a read operation hasn't been used yet; The EOF flag will only be set after a read operation returns a value indicating an error. This is where an error lies. In your case, you need to loop on the return value of fscanf(fp,"%s\n",word); rather than the return value of feof. For example:
while (fscanf(fp, "%s", word) == 1) {
/* ... */
}
if (feof(fp)) {
/* The loop ended due to EOF */
}
else if (ferror(fp)) {
/* The loop ended due to some file input error */
}
else {
/* The loop ended because the input was invalid
* (this applies to input where a conversion is
* required eg. the conversion in %d, %u, %f, etc... */
}
To elaborate, feof is only for determining why the last read failed!
The reason this would cause such a warning in the case of a blank file is that word would contain indeterminate information.
Additionally, freeNode(&root); is erroneous because free is only to be called on pointers that are returned by calloc, realloc and malloc.
node root = {false,{NULL}};
is not allocated on the heap, but then you try to free it like it is
unload(void)
{
freeNode(&root);
Related
I have a memory leak issue, according to check50 and valgrind. It looks like I'm trying to use a variable that might not have a value. Everything else works correctly, according to check50, the only issue is with memory. Here is valgrind's error message:
==3243== Conditional jump or move depends on uninitialised value(s)
==3243== at 0x520A60F: tolower (ctype.c:46)
==3243== by 0x4010CD: check (dictionary.c:36)
==3243== by 0x400CD9: main (speller.c:112)
==3243== Uninitialised value was created by a stack allocation
==3243== at 0x4008E4: main (speller.c:21)
==3243==
==3243== Use of uninitialised value of size 8
==3243== at 0x520A623: tolower (ctype.c:46)
==3243== by 0x4010CD: check (dictionary.c:36)
==3243== by 0x400CD9: main (speller.c:112)
==3243== Uninitialised value was created by a stack allocation
==3243== at 0x4008E4: main (speller.c:21)
==3243==
WORDS MISSPELLED: 0
WORDS IN DICTIONARY: 143091
WORDS IN TEXT: 6
TIME IN load: 1.46
TIME IN check: 0.00
TIME IN size: 0.00
TIME IN unload: 0.21
TIME IN TOTAL: 1.67
==3243==
==3243== HEAP SUMMARY:
==3243== in use at exit: 0 bytes in 0 blocks
==3243== total heap usage: 143,097 allocs, 143,097 frees, 8,023,462 bytes allocated
==3243==
==3243== All heap blocks were freed -- no leaks are possible
==3243==
==3243== For counts of detected and suppressed errors, rerun with: -v
==3243== ERROR SUMMARY: 492 errors from 2 contexts (suppressed: 0 from 0)
Asking for help...
==3243== Conditional jump or move depends on uninitialised value(s)
Looks like you're trying to use a variable that might not have a value? Take a closer look at line 36 of dictionary.c.
Here is my code (I would love if you could help ;))
// Implements a dictionary's functionality
#include <stdbool.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include "dictionary.h"
#define HASHTABLE_SIZE 65536
// Represents a node in a hash table
typedef struct node
{
char word[LENGTH + 1];
struct node *next;
}
node;
// Number of buckets in hash table
const unsigned int N = HASHTABLE_SIZE;
// Hash table
node *table[N];
unsigned int totalWords = 0;
// Returns true if word is in dictionary else false
bool check(const char *word)
{
//Initialize lower case word
char lcword[LENGTH + 1];
for (int i = 0; i < LENGTH + 1; i++)
lcword[i] = tolower(word[i]);
node *cursor = table[hash(lcword)];
while (cursor != NULL)
{
if (strcasecmp(word, cursor->word) == 0)
{
return true;
}
cursor = cursor->next;
}
return false;
}
// Hashes word to a number
unsigned int hash(const char *word)
{
//https://www.reddit.com/r/cs50/comments/1x6vc8/pset6_trie_vs_hashtable/cf9nlkn/
unsigned int hash_value = 0;
for (int i = 0, n = strlen(word); i < n; i++)
hash_value = (hash_value << 2) ^ word[i];
return hash_value % HASHTABLE_SIZE;
}
// Loads dictionary into memory, returning true if successful else false
bool load(const char *dictionary)
{
FILE *dicfile = fopen(dictionary, "r");
char *word = malloc(LENGTH + 1);
//Check if word is null
if (word == NULL)
return false;
//Check if the fopen function opened a not NULL file
if (dicfile == NULL)
return false;
//Iterate over dictionary until fscanf return EOF (meaning it's the end of the file)
while (fscanf(dicfile, "%s", word) != EOF)
{
//Create a node to store the current word
node *new_node = malloc(sizeof(node));
if (new_node == NULL)
return false;
//Copy the new_node's word into the current word
strcpy(new_node->word, word);
//Get the index (hash the current word and store it in n)
int n = hash(new_node->word);
//Insert the new_node into the linked list
new_node->next = table[n];
table[n] = new_node;
//Add to the total number of words in the text file
totalWords++;
}
fclose(dicfile);
free(word);
return true;
}
// Returns number of words in dictionary if loaded else 0 if not yet loaded
unsigned int size(void)
{
return totalWords;
}
// Unloads dictionary from memory, returning true if successful else false
bool unload(void)
{
for (int i = 0; i < HASHTABLE_SIZE; i++)
{
node* cursor = table[i];
node* tmp;
while (cursor != NULL)
{
tmp = cursor;
cursor = cursor->next;
free(tmp);
}
free(cursor);
}
return true;
}
And if you want to know, line 36 of my code is this one: lcword[i] = tolower(word[i]);
Thank you!!!
regarding:
for (int i = 0; i < LENGTH + 1; i++)
in C, the valid range of indexes into an array are 0...(number of elements in array-1 ) Note the -1
Note: the macro: LENGTH is not defined anywhere in the posted code.
the function: check() is implemented in the posted code, but never called.
regarding:
for ( int i = 0, n = strlen(word); i < n; i++)
The function: strlen() returns a size_t, so the statement should be:
for ( size_t i = 0, n = strlen(word); i < n; i++ )
The function: size() is implemented but never called.
it is a very poor programming practice to include header files those contents are not used. for instance:
#include <strings.h>
regarding;
if (dicfile == NULL)
return false;
This should be immediately after;
FILE *dicfile = fopen(dictionary, "r");
and should include the statement:
perror( "fopen to read dictionary file failed" );
regarding:
while (fscanf(dicfile, "%s", word) != EOF)
there are other reasons that a call to fscanf() can fail. Should be checking for success.
the %s input format conversion specifier can input more characters than there are in the array word(). Also, that specifier always appends a NUL byte to the input. To avoid any buffer overflow problems, always use a modifier that 1 less than the length of the buffer. Suggest:
while (fscanf(dicfile, "%" LENGTH "s", word) == 1)
regarding your question about a memory leak:
The posted code modifies pointers (by overlaying them) in table[] without first checking of there is already an active entry there. If there is an active entry already at the 'hashed' index into the table, then the new entry must be linked after (could be linked before) the current entry, at the same index into table[]
Of course, when an active entry is overlayed, the result is a memory leak.
Before I get torn apart for not looking at the plethora of Valgrind questions on here: I did. I spent a good while looking, and all I've found were people who didn't malloc() the correct number of bytes. If I am incorrect and this is a duplicate, I will gladly accept your reference.
In this program, I am using a tree to create a spell-checker. There are many things that need to be fixed in my code, but the memory leak is the only one I really need help figuring out. (A lot of this code is temporary just so it will compile so that I can fix the memory leak.)
The issue is that I am fairly sure I am allocating the correct amount of space for my nodes, and I think Valgrind confirms this, because I only have 2 not-freed blocks (out of 365,371 allocs).
Anyway, I will post the entirety of the code (in case anyone needs the full context), but the relevant parts I presume are the load function and the clear function, where I allocate and free the memory, respectively.
/**
* Implements a dictionary's functionality.
*/
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "dictionary.h"
// number of characters we are using (a-z and ')
#define LETTERS 27
// max guaranteed number of nonnegative char values that exist
#define CHARVALUES 128
// create node structure for trie
typedef struct node
{
struct node *children[LETTERS];
bool is_word;
}
node;
// create root node for trie
node *root;
// stores the size of our dictionary
unsigned int dict_size = 0;
/**
* Returns true if word is in dictionary else false.
*/
bool check(const char *word)
{
// keeps track of where we are; starts with root for each new word
node *current_node = root;
while (*word != '\0')
{
// indices: 'a' -> 0, ..., 'z' -> 25, '\' -> 26
int index = (tolower(*word) - 'a') % CHARVALUES;
if (index >= LETTERS - 1)
{
// by assumption, the char must be '\'' if not '\n' or a letter
index = LETTERS - 1;
}
// if the node we need to go to is NULL, the word is not here
if (current_node->children[index] == NULL)
{
return false;
}
// go to the next logical node, and look at the next letter of the word
current_node = current_node->children[index];
word++;
}
}
return current_node->is_word;
}
/**
* Loads dictionary into memory. Returns true if successful else false.
*/
bool load(const char *dictionary)
{
FILE *inptr = fopen(dictionary, "r");
if (inptr == NULL)
{
return false;
}
// allocate memory for the root node
root = malloc(sizeof(node));
// store first letter (by assumption, it must be a lowercase letter)
char letter = fgetc(inptr);
// stores indices corresponding to letters
int index = 0;
/**
* we can assume that there is at least one word; we will execute the loop
* and assign letter a new value at the end. at the end of each loop, due
* to the inside loop, letter will be a newline; we know the EOF in the
* dictionary follows a newline, so the loop will terminate appropriately
*/
do
{
// keeps track of where we are; starts with root for each new word
node *current_node = root;
// this loop will only execute if our character is a letter or '\''
while (letter != '\n')
{
// indices: 'a' -> 0, ..., 'z' -> 25, '\' -> 26
index = (letter - 'a') % CHARVALUES;
if (index >= LETTERS - 1)
{
// by assumption, the char must be '\'' if not '\n' or a letter
index = LETTERS - 1;
}
// allocate memory for a node if we have not done so already
if (current_node->children[index] == NULL)
{
current_node->children[index] = malloc(sizeof(node));
// if we cannot allocate the memory, unload and return false
if (current_node->children[index] == NULL)
{
unload();
return false;
}
}
// go to the appropriate node for the next letter in our word
current_node = current_node->children[index];
// get the next letter
letter = fgetc(inptr);
}
// after each linefeed, our current node represents a dictionary word
current_node->is_word = true;
dict_size++;
// get the next letter
letter = fgetc(inptr);
}
while (letter != EOF);
fclose(inptr);
// if we haven't returned false yet, then loading the trie must have worked
return true;
}
/**
* Returns number of words in dictionary if loaded else 0 if not yet loaded.
*/
unsigned int size(void)
{
return dict_size;
}
void clear(node *head)
{
for (int i = 0; i < LETTERS; i++)
{
if (head->children[i] != NULL)
{
clear(head->children[i]);
}
}
free(head);
}
/**
* Unloads dictionary from memory. Returns true if successful else false.
*/
bool unload(void)
{
clear(root);
return true;
}
The relevant valgrind output is the following:
==18981== HEAP SUMMARY:
==18981== in use at exit: 448 bytes in 2 blocks
==18981== total heap usage: 365,371 allocs, 365,369 frees, 81,843,792 bytes allocated
==18981==
==18981== 448 (224 direct, 224 indirect) bytes in 1 blocks are definitely lost in loss record 2 of 2
==18981== at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==18981== by 0x4011B0: load (dictionary.c:111)
==18981== by 0x4008CD: main (speller.c:40)
==18981==
==18981== LEAK SUMMARY:
==18981== definitely lost: 224 bytes in 1 blocks
==18981== indirectly lost: 224 bytes in 1 blocks
==18981== possibly lost: 0 bytes in 0 blocks
==18981== still reachable: 0 bytes in 0 blocks
==18981== suppressed: 0 bytes in 0 blocks
So, my interpretation of this output is that, in the following block of code:
if (current_node->children[index] == NULL)
{
current_node->children[index] = malloc(sizeof(node));
// if we cannot allocate the memory, unload and return false
if (current_node->children[index] == NULL)
{
unload();
return false;
}
}
the malloc statement (which is indeed line dictionary.c:111) is executed twice such that the allocated memory is never freed. (Is this correct?) Now, that leads me to think that the real problem lies with my clear function, i.e. that it is written poorly and does not clear every node of my trie.
However, I've stared at the code for hours and I literally cannot see anything wrong with it. (I'm sure a lot is; I'm just not too good at this.)
Any help with this would be greatly appreciated.
For starters:
The code misses to initialise the member array children to all NULLs for each node malloc()ed freshly. malloc() does not perform any initialisation on the memory allocated. It just contains "garbage".
So this here
if (current_node->children[index] == NULL)
{
becomes a "random" decision, if not provoking undefined behaviour by itself already.
(BTW, the "Invalid Read/Writes" your question's title mentions, and which you do not show us, most likely also referred to exactly this very line of code above ...)
To fix this you could use something like this:
#include <stdlib.h> (/* for malloc() */
#include <errno.h> (/* for errno */
/* Allocate and initialises a new node. */
/* Returns 0 on success and -1 on failure. */
int node_create(node ** pn)
{
int result = 0; /* Be optimistic. */
if (NULL == pn)
{
errno = EINVAL;
result = -1;
}
else
{
node * n = malloc(sizeof *n);
if (NULL == n)
{
result = -1;
}
else
{
for (size_t i = 0; i < LETTERS; ++i)
{
n -> children[i] = NULL;
}
n -> is_word = 0;
(*pn) = n;
}
}
return result;
}
And use it like this:
...
if (-1 == node_create(&root))
{
perror("node_create() failed");
return false;
}
In a course I take, there's a pset that involves reading a dictionary file, and creating a hash-table out of it, checking the correct spelling of words in another file by means of checking whether a word in text can be found in the hashtable. Then unloading it from memory.
There are two files in the program. One file that I didn't write (and has no problems for sure), that runs first and calls the functions of the .c file that I wrote:
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "dictionary.h"
#define DICSIZE 10000
typedef struct node
{
char* word;
struct node* next;
}
node;
typedef struct table
{
node** table;
unsigned int wordCount;
}
table;
// initialize table for scope purposes
struct table* hashTable = NULL;
// declaration of the hash function
unsigned long hashDJB2(unsigned char *str);
// declaration of the string duplication function
char* mystrdup(const char* s);
// declaration of the lowercase function
char* returnLowerCase(const char* str);
/**
* Returns true if word is in dictionary else false.
*/
bool check(const char* word)
{
node* list = NULL;
char* wrd = returnLowerCase(word);
unsigned long hashValue = hashDJB2((unsigned char*) wrd);
for (list = hashTable->table[hashValue]; list != NULL; list = list->next) {
if (strcmp(list->word, wrd) == 0) {
return true;
}
}
free(wrd);
return false;
}
/**
* Loads dictionary into memory. Returns true if successful else false.
*/
bool load(const char* dictionary)
{
// allocation of memory for the table structure
if ((hashTable = malloc(sizeof(struct table))) == NULL) {
printf("Error allocating memory for table pointer\n");
return false;
}
// allocate pointers to table itself
if ((hashTable->table = malloc((sizeof(node*) * DICSIZE) + sizeof(unsigned int))) == NULL) {
printf("Error allocating memory for heads of list\n");
return false;
}
// initialize head nodes to NULL
for (int i = 0; i < DICSIZE; i++) {
hashTable->table[i] = NULL;
}
// initialize wordCount to zero
hashTable->wordCount = 0;
/*
* Till now, was the creation of the backbone structure.
* Now, implementation of hash() function and loading the words
* onto the table.
*/
FILE* dp;
dp = fopen(dictionary, "r");
if (dp == NULL) {
printf("Error in reading from dictionary\n");
return false;
}
// reading words from dictionary
char newWord[LENGTH + 1];
while (fscanf(dp, "%s", newWord) == 1) {
// determine the hash value of the
unsigned long hashValue = hashDJB2((unsigned char*) newWord);
// in the pset, they say i don't have to check whether the word already exists
// if (! check((const char*) newWord)) {
// if not, insert a new node:
// attempt to create a new node
node* newNode = NULL;
if ((newNode = malloc(sizeof(node))) == NULL) {
printf("Error: couldnt allocate a new node\n");
return false;
}
// insert new node to the beginning of the list
newNode->word = mystrdup(newWord);
newNode->next = hashTable->table[hashValue];
hashTable->table[hashValue] = newNode;
hashTable->wordCount++;
//}
}
//free(dp);
return true;
}
/**
* Returns number of words in dictionary if loaded else 0 if not yet loaded.
*/
unsigned int size(void)
{
unsigned int wordCount = hashTable->wordCount;
return wordCount;
}
/**
* Unloads dictionary from memory. Returns true if successful else false.
*/
bool unload(void)
{
node* head = NULL;
long i = 0;
for (head = hashTable->table[i]; head != NULL; head = hashTable->table[++i]) {
node* list = NULL;
for (list = head; list != NULL; list = head) {
head = head->next;
free(list->word);
free(list);
}
}
free(hashTable->table);
free(hashTable);
return true;
}
// djb2 algorithm, created by Dan Bernstein
unsigned long hashDJB2(unsigned char *str)
{
unsigned long hash = 5381;
int c;
while ((c = *str++))
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
return hash % DICSIZE;
}
char* mystrdup(const char* s)
{
char* p = malloc(sizeof(char) * strlen(s)+1);
if (p) strcpy(p, s);
return p;
}
char* returnLowerCase(const char* str) {
int len = strlen(str);
char* p = malloc(sizeof(char) * (len + 1));
for (int i = 0; i < len; i++) {
p[i] = tolower(str[i]);
}
p[len + 1] = '\0'; // this line made the program crash. It was fixed already.
return p;
}
First the "speller.c" (which I got from the pset) calls the load() function, then calls the check() function several tens of thousands times (depends on the number of words in the text), then unload(). I have a problem with the checking and unloading part. When the text is small the check() functions properly, but when It gets larger I get an error like this:
speller: malloc.c:2372: sysmalloc: Assertion `(old_top == (((mbinptr) (((char *) &((av)->bins[((1) - 1) * 2])) - __builtin_offsetof (struct malloc_chunk, fd)))) && old_size == 0) || ((unsigned long) (old_size) >= (unsigned long)((((__builtin_offsetof (struct malloc_chunk, fd_nextsize))+((2 *(sizeof(size_t))) - 1)) & ~((2 *(sizeof(size_t))) - 1))) && ((old_top)->size & 0x1) && ((unsigned long) old_end & pagemask) == 0)' failed.
Aborted (core dumped)
The first problem is, It seems that there is a problem in the way I allocate memory. The other problem is that I don't manage to free all the memory I've allocated.
For some reason, valgrinds --track-origins=yes doesn't work so all I've got from It is this:
==3852==
==3852== HEAP SUMMARY:
==3852== in use at exit: 101,650 bytes in 18,547 blocks
==3852== total heap usage: 305,376 allocs, 286,829 frees, 2,730,448 bytes allocated
==3852==
==3852== LEAK SUMMARY:
==3852== definitely lost: 101,298 bytes in 18,546 blocks
==3852== indirectly lost: 0 bytes in 0 blocks
==3852== possibly lost: 0 bytes in 0 blocks
==3852== still reachable: 352 bytes in 1 blocks
==3852== suppressed: 0 bytes in 0 blocks
==3852== Rerun with --leak-check=full to see details of leaked memory
==3852==
==3852== For counts of detected and suppressed errors, rerun with: -v
==3852== Use --track-origins=yes to see where uninitialised values come from
==3852== ERROR SUMMARY: 57191 errors from 5 contexts (suppressed: 0 from 0)
I would love to get some help at that.
One bug (there may be others), in returnLowerCase():
p[len + 1] = '\0';
should be:
p[len] = '\0';
(You have allocated len + 1 chars, so the valid indices are 0..len inclusive, with the actual string being at indices 0..len-1 and the \0 terminator at index len.)
So, I have been assigned a task to implement a free list. A list where items that are going to be free:d are added to and later the list is free:d in one go. I have written the following:
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct list_t list_t;
struct list_t {
list_t* succ;
list_t* pred;
void* data;
};
list_t* free_list;
void free_list_memory(void)
{
list_t *p , *q;
p = free_list;
while (p != NULL) {
q = p->succ;
free(p);
p = q;
}
}
void add_to_free_list(list_t* add) {
if (free_list != NULL) {
add->pred = free_list->pred;
add->succ = free_list;
free_list->pred->succ = add;
free_list->pred = add;
} else {
free_list = add;
add->succ = add;
add->pred = add;
}
}
static double sec(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec + 1e-6 * tv.tv_usec;
}
int empty(list_t* list)
{
return list == list->succ;
}
list_t *new_list(void* data)
{
list_t* list;
list = malloc(sizeof(list_t));
assert(list != NULL);
list->succ = list->pred = list;
list->data = data;
return list;
}
void add(list_t* list, void* data)
{
list_t* link;
list_t* temp;
link = new_list(data);
list->pred->succ= link;
link->succ = list;
temp = list->pred;
list->pred = link;
link->pred = temp;
}
void take_out(list_t* list)
{
list->pred->succ = list->succ;
list->succ->pred = list->pred;
list->succ = list->pred = list;
}
void* take_out_first(list_t* list)
{
list_t* succ;
void* data;
if (list->succ->data == NULL)
return NULL;
data = list->succ->data;
succ = list->succ;
take_out(succ);
free(succ);
return data;
}
static size_t nextsize()
{
#if 1
return rand() % 4096;
#else
size_t size;
static int i;
static size_t v[] = { 24, 520, 32, 32, 72, 8000, 16, 24, 212 };
size = v[i];
i = (i + 1) % (sizeof v/ sizeof v[0]);
return size;
#endif
}
static void fail(char* s)
{
fprintf(stderr, "check: %s\n", s);
abort();
}
int main(int ac, char** av)
{
int n = 50; /* mallocs in main. */
int n0;
list_t* head;
double begin;
double end;
double t = 2.5e-9;
if (ac > 1)
n = atoi(av[1]);
n0 = n;
head = new_list(NULL);
printf("check starts\n");
begin = sec();
while (n > 0) {
add(head, malloc(nextsize()));
n -= 1;
if ((n & 1) && !empty(head)) {
add_to_free_list(take_out_first(head)); //before free(take_out_first(head))
}
}
printf("Done");
while (!empty(head))
add_to_free_list(take_out_first(head)); //before free(take_out_first(head))
free_list_memory(); //added line
end = sec();
printf("check is ready\n");
printf("total = %1.3lf s\n", end-begin);
printf("m+f = %1.3g s\n", (end-begin)/(2*n0));
printf("cy = %1.3lf s\n", ((end-begin)/(2*n0))/t);
return 0;
}
Running the code gives:
a.out(10009,0x7fff79ec0300) malloc: *** error for object 0x7fe160c04b20: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
DoneAbort trap: 6
Running in valgrind gives:
check starts
==10011== Invalid read of size 8
==10011== at 0x1000009B8: free_list_memory (check.c:20)
==10011== by 0x100000D8B: main (check.c:163)
==10011== Address 0x10081e270 is 0 bytes inside a block of size 423 free'd
==10011== at 0x10000894F: free (in /usr/local/Cellar/valgrind/HEAD/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
==10011== by 0x1000009CA: free_list_memory (check.c:21)
==10011== by 0x100000D8B: main (check.c:163)
==10011==
==10011== Invalid free() / delete / delete[] / realloc()
==10011== at 0x10000894F: free (in /usr/local/Cellar/valgrind/HEAD/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
==10011== by 0x1000009CA: free_list_memory (check.c:21)
==10011== by 0x100000D8B: main (check.c:163)
==10011== Address 0x10081e270 is 0 bytes inside a block of size 423 free'd
==10011== at 0x10000894F: free (in /usr/local/Cellar/valgrind/HEAD/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
==10011== by 0x1000009CA: free_list_memory (check.c:21)
==10011== by 0x100000D8B: main (check.c:163)
==10011==
==10011==
==10011== More than 10000000 total errors detected. I'm not reporting any more.
==10011== Final error counts will be inaccurate. Go fix your program!
==10011== Rerun with --error-limit=no to disable this cutoff. Note
==10011== that errors may occur in your program without prior warning from
==10011== Valgrind, because errors are no longer being displayed.
==10011==
^C==10011==
==10011== HEAP SUMMARY:
==10011== in use at exit: 38,785 bytes in 423 blocks
==10011== total heap usage: 602 allocs, 6,176,342 frees, 149,153 bytes allocated
==10011==
==10011== LEAK SUMMARY:
==10011== definitely lost: 0 bytes in 0 blocks
==10011== indirectly lost: 0 bytes in 0 blocks
==10011== possibly lost: 0 bytes in 0 blocks
==10011== still reachable: 4,120 bytes in 2 blocks
==10011== suppressed: 34,665 bytes in 421 blocks
==10011== Rerun with --leak-check=full to see details of leaked memory
==10011==
==10011== For counts of detected and suppressed errors, rerun with: -v
==10011== ERROR SUMMARY: 10000000 errors from 2 contexts (suppressed: 0 from 0)
Now, I don't understand what's wrong since I am removing elements from the list that have been created using malloc...
As WhozCraig diagnosed, the problem is more that the code does not stop when it gets to the end of the free list because it is a circular list and the termination condition is looking for null pointers that don't exist.
You can fix most of the problems by rewriting free_list_memory() as:
static void free_list_memory(void)
{
if (free_list == 0)
return;
list_t *p = free_list;
do
{
list_t *q = p->succ;
// free(p->data); // Removed: see commentary below!
free(p);
p = q;
} while (p != free_list);
}
With that change, I get one unfreed memory block of size 24 (on a 64-bit build). So there is one list_t not being freed. That item is in head; it happens to have a NULL data pointer, so you can fix that final leak with:
free(head);
at the end of main().
Surprise!
At one time, I commented:
You also need to free the data as well as the list entry.
Somewhat to my surprise, on retesting, that isn't necessary. In fact, on my machine, in free_list_memory(), all the data pointers are null. That's actually unfortunate — malloc() is returning zeroed data. What actually happens is that the list freeing code releases the original list_t pointer that held the data, and then adds the original data pointer to the free list, treating it as a list_t structure. That gets iffy if the size of the allocated block (a random number from nextsize()) is smaller than a list_t.
To fix the problems and get a really clean run, even with different numbers of entries, I created this instrumented version of the code (with WhozCraig's function active).
Revised code
Note that the code ensures that the space allocated is at least as big as a list_t structure; that is crucial to avoid memory access errors (on longer runs; I used 200 to get problems with small allocations without adding sizeof(list_t) because it so happens that the first 50 allocations are always big enough).
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
typedef struct list_t list_t;
struct list_t {
list_t* succ;
list_t* pred;
void* data;
};
list_t* free_list;
static
void free_list_memory(void)
{
if (!free_list)
return;
// terminate "last" node (the node that refers
// back to the node pointed to by free_list,
// including a potential self-referencing node)
free_list->pred->succ = NULL;
// now run the loop. uses free_list as the iterator
// as it will finish with NULL, as it will be empty.
while (free_list)
{
void *p = free_list;
if (free_list->data == 0)
printf("2 %p: data is null\n", free_list);
//free(free_list->data); // Not necessary
free_list = free_list->succ;
free(p);
}
}
#if 0
static void free_list_memory(void)
{
if (free_list == 0)
return;
list_t *p = free_list;
do
{
list_t *q = p->succ;
// free(p->data); // Removed: see commentary below!
free(p);
p = q;
} while (p != free_list);
}
#endif
#if 0
/* Broken code from question - does not handle circular list */
void free_list_memory(void)
{
list_t *p , *q;
p = free_list;
while (p != NULL) {
q = p->succ;
free(p);
p = q;
}
}
#endif
static
void add_to_free_list(list_t* add) {
if (free_list != NULL) {
add->pred = free_list->pred;
add->succ = free_list;
free_list->pred->succ = add;
free_list->pred = add;
} else {
free_list = add;
add->succ = add;
add->pred = add;
}
add->data = 0; // Added to avoid access to uninitialized data warning from valgrind
}
static double sec(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec + 1e-6 * tv.tv_usec;
}
static
int empty(list_t* list)
{
return list == list->succ;
}
static
list_t *new_list(void* data)
{
list_t* list;
list = malloc(sizeof(list_t));
printf("1: data address is %p\n", data);
assert(list != NULL);
list->succ = list->pred = list;
list->data = data;
return list;
}
static
void add(list_t* list, void* data)
{
list_t* link;
list_t* temp;
link = new_list(data);
list->pred->succ= link;
link->succ = list;
temp = list->pred;
list->pred = link;
link->pred = temp;
}
static
void take_out(list_t* list)
{
list->pred->succ = list->succ;
list->succ->pred = list->pred;
list->succ = list->pred = list;
}
static
void* take_out_first(list_t* list)
{
list_t* succ;
void* data;
if (list->succ->data == NULL)
{
printf("3: %p - data is null\n", list->succ);
return NULL;
}
data = list->succ->data;
succ = list->succ;
take_out(succ);
free(succ);
return data;
}
static size_t nextsize(void)
{
#if 1
size_t v = rand() % 4096 + sizeof(list_t);
printf("Size: %zu\n", v);
return v;
//return rand() % 4096;
#else
size_t size;
static int i;
static size_t v[] = { 24, 520, 32, 32, 72, 8000, 16, 24, 212 };
size = v[i];
i = (i + 1) % (sizeof v/ sizeof v[0]);
return size;
#endif
}
#if 0
static void fail(char* s)
{
fprintf(stderr, "check: %s\n", s);
abort();
}
#endif
int main(int ac, char** av)
{
int n = 50; /* mallocs in main. */
int n0;
list_t* head;
double begin;
double end;
double t = 2.5e-9;
if (ac > 1)
n = atoi(av[1]);
n0 = n;
head = new_list(NULL);
printf("check starts\n");
begin = sec();
while (n > 0) {
add(head, malloc(nextsize()));
n -= 1;
if ((n & 1) && !empty(head)) {
add_to_free_list(take_out_first(head)); //before free(take_out_first(head))
}
}
printf("Done\n");
while (!empty(head))
add_to_free_list(take_out_first(head)); //before free(take_out_first(head))
free_list_memory(); //added line
free(head);
end = sec();
printf("Check is ready\n");
printf("total = %1.3lf s\n", end-begin);
printf("m+f = %1.3g s\n", (end-begin)/(2*n0));
printf("cy = %1.3lf s\n", ((end-begin)/(2*n0))/t);
return 0;
}
The compilation options I habitually use (here I suppressed optimization to avoid having everything inlined in main()) require declarations of non-static functions, so I added static to each function except main() that was not already static. I also added (void) in place of () when necessary.
$ gcc -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
> -Wold-style-definition -Werror crashing.c -o crashing
$
Valgrind output
$ valgrind --leak-check=full --suppressions=suppressions crashing 10
==41722== Memcheck, a memory error detector
==41722== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==41722== Using Valgrind-3.11.0.SVN and LibVEX; rerun with -h for copyright info
==41722== Command: crashing 10
==41722==
--41722-- UNKNOWN mach_msg unhandled MACH_SEND_TRAILER option
--41722-- UNKNOWN mach_msg unhandled MACH_SEND_TRAILER option (repeated 2 times)
--41722-- UNKNOWN mach_msg unhandled MACH_SEND_TRAILER option (repeated 4 times)
1: data address is 0x0
check starts
Size: 447
1: data address is 0x10083f3e0
Size: 2825
1: data address is 0x100842440
Size: 3313
1: data address is 0x100842f90
Size: 3138
1: data address is 0x100843cd0
Size: 1946
1: data address is 0x10083f760
Size: 2784
1: data address is 0x100844960
Size: 3824
1: data address is 0x100845480
Size: 2582
1: data address is 0x100846410
Size: 3931
1: data address is 0x100846ed0
Size: 1125
1: data address is 0x100847ed0
Done
2 0x10083f3e0: data is null
2 0x100842440: data is null
2 0x100842f90: data is null
2 0x100843cd0: data is null
2 0x10083f760: data is null
2 0x100844960: data is null
2 0x100845480: data is null
2 0x100846410: data is null
2 0x100846ed0: data is null
2 0x100847ed0: data is null
Check is ready
total = 0.010 s
m+f = 0.000487 s
cy = 194959.641 s
==41722==
==41722== HEAP SUMMARY:
==41722== in use at exit: 39,132 bytes in 430 blocks
==41722== total heap usage: 528 allocs, 98 frees, 71,359 bytes allocated
==41722==
==41722== LEAK SUMMARY:
==41722== definitely lost: 0 bytes in 0 blocks
==41722== indirectly lost: 0 bytes in 0 blocks
==41722== possibly lost: 0 bytes in 0 blocks
==41722== still reachable: 26,034 bytes in 311 blocks
==41722== suppressed: 13,098 bytes in 119 blocks
==41722== Reachable blocks (those to which a pointer was found) are not shown.
==41722== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==41722==
==41722== For counts of detected and suppressed errors, rerun with: -v
==41722== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 15 from 15)
$
And you can see from the printed pointer values that the data added to the free list is indeed the data rather than the list_t structures holding the data.
It would be more conventional (and arguably sensible) to simply move the entire list_t structure from the active list to the free list, rather than freeing the list_t structure and using the data as the list_t structure on the free list.
Also, you should probably record the size of the data so you know whether the stuff on the free list can be used for a particular allocation.
Assuming the rest of your code is correct (and that is a big assumption), your free_memory_list function doesn't account for the fact the list is circular. The last node succ member refers back to the address held in free_list.
If that is the case, this is more akin to what you need:
void free_list_memory(void)
{
if (!free_list)
return;
// terminate "last" node (the node that refers
// back to the node pointed to by free_list,
// including a potential self-referencing node)
free_list->pred->succ = NULL;
// now run the loop. uses free_list as the iterator
// as it will finish with NULL, as it will be empty.
while (free_list)
{
void *p = free_list;
free_list = free_list->succ;
free(p);
}
}
I have to be honest. I did not diagnose the rest of this, but this is a glaring problem if this list is indeed circular.
Best of luck.
Why are you using double linked (closed loop) list for your free list, if only go in one direction.
Your void free_list_memory(void) function crashes when trying to run a second time over your list.
Maybe this helps:
void add_to_free_list(list_t* add) {
if (free_list != NULL) {
free_list->succ = add;
add->succ = NULL;
} else {
free_list = add;
add->succ = NULL;
}
}
I've been working on a project for school (Due tonight!), and I'm having some serious memory issues. I'm fairly new to C and am still being thrown for a loop when it comes to mallocing pointers and whatnot, so I could really use some help.
The code is as follows. The order of the files is LinkedLists.h (Header for LinkedList module), LinkedLists.c (LinkedList module), and TestList.c (Main module).
/******************************************************************************
* Base struct to contain data structure element information: deterimined by
* the application needs.
******************************************************************************/
#ifndef _LINKED_LISTS_H_
#define _LINKED_LISTS_H_
typedef struct ElementStructs
{
int ElementPosition;
char* ElementValue;
} ElementStructs;
/************** Nothing else in the module needs to be modified *************/
/******************************************************************************
* Base struct of list nodes, contains user information and link pointers.
* The "ElementStructs" typemark must be defined based on specific needs of the
* application.
******************************************************************************/
typedef struct LinkedListNodes
{
/* The user information field */
ElementStructs *ElementPtr;
/* Link pointers */
struct LinkedListNodes *Next;
struct LinkedListNodes *Previous;
} LinkedListNodes;
/******************************************************************************
* Base struct used to manage the linked list data structure.
******************************************************************************/
typedef struct LinkedLists
{
/* Number of elements in the list */
int NumElements;
/* Pointer to the front of the list of elements, possibly NULL */
struct LinkedListNodes *FrontPtr;
/* Pointer to the end of the list of elements, possibly NULL */
struct LinkedListNodes *BackPtr;
} LinkedLists;
/******************************************************************************
* Initialized the linked list data structure
******************************************************************************/
void InitLinkedList(LinkedLists *ListPtr);
/******************************************************************************
* Adds a record to the front of the list.
******************************************************************************/
void AddToFrontOfLinkedList(LinkedLists *ListPtr, ElementStructs *DataPtr);
/******************************************************************************
* Adds a record to the back of the list.
******************************************************************************/
void AddToBackOfLinkedList(LinkedLists *ListPtr, ElementStructs *DataPtr);
/******************************************************************************
* Removes (and returns) a record from the front of the list ('works' even on
* an empty list by returning NULL).
******************************************************************************/
ElementStructs *RemoveFromFrontOfLinkedList(LinkedLists *ListPtr);
/******************************************************************************
* Removes (and returns) a record from the back of the list ('works' even on
* an empty list by returning NULL).
******************************************************************************/
ElementStructs *RemoveFromBackOfLinkedList(LinkedLists *ListPtr);
/******************************************************************************
* De-allocates the linked list and resets the struct fields as if the
* list was empty.
******************************************************************************/
void DestroyLinkedList(LinkedLists *ListPtr);
#endif /* _LINKED_LISTS_H_ */
LinkedLists.c:
/******************************************************************************
* Extracts and prints the first and last 6 elements from the specified data
* set and prints the total number of words in the input file. Utilizes the
* LinkedList module as specified in LinkedLists.h
* Written by xxxxxxx
******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include "LinkedLists.h"
void InitLinkedList(LinkedLists *ListPtr)
{
ListPtr->NumElements = 0;
ListPtr->FrontPtr = NULL;
ListPtr->BackPtr = NULL;
}
void AddToFrontOfLinkedList(LinkedLists *ListPtr, ElementStructs
*DataPtr)
{
/* If there are no other elements, create new node and add it,
* assigning it to both the front and back pointers */
if(ListPtr->NumElements == 0)
{
ListPtr->FrontPtr = malloc(sizeof(LinkedListNodes));
ListPtr->FrontPtr->ElementPtr = DataPtr;
ListPtr->FrontPtr->Next = NULL;
ListPtr->FrontPtr->Previous = NULL;
ListPtr->BackPtr = ListPtr->FrontPtr;
}
/* If there are other elements, create new node and add it to the front
* while retaining previous node order */
else
{
/* Initialize new LinkedListNode */
ListPtr->FrontPtr->Previous = malloc(sizeof(LinkedListNodes));
ListPtr->FrontPtr->Previous->ElementPtr = DataPtr;
ListPtr->FrontPtr->Previous->Next = ListPtr->FrontPtr;
ListPtr->FrontPtr->Previous->Previous = NULL;
/* Assign newly initialized node as front node of LinkedList */
ListPtr->FrontPtr = ListPtr->FrontPtr->Previous;
}
/* List size plus one */
(ListPtr->NumElements)++;
}
void AddToBackOfLinkedList(LinkedLists *ListPtr, ElementStructs
*DataPtr)
{
/* If there are no other elements, create new node and add it,
* assigning it to both the front and back pointers */
if(ListPtr->NumElements == 0)
{
ListPtr->FrontPtr = malloc(sizeof(LinkedListNodes));
ListPtr->FrontPtr->ElementPtr = DataPtr;
ListPtr->FrontPtr->Next = NULL;
ListPtr->FrontPtr->Previous = NULL;
ListPtr->BackPtr = ListPtr->FrontPtr;
/*printf("Adding %s\n", DataPtr->ElementValue);*/
}
/* If there are other elements, create new node and add it to the back
* while retaining previous node order */
else
{
/* Initialize new LinkedListNode */
ListPtr->BackPtr->Next = malloc(sizeof(LinkedListNodes));
ListPtr->BackPtr->Next->ElementPtr = DataPtr;
ListPtr->BackPtr->Next->Previous = ListPtr->BackPtr;
ListPtr->BackPtr->Next->Previous = ListPtr->BackPtr;
ListPtr->BackPtr->Next->Next = NULL;
/* Assign newly initialized node as back node of LinkedList */
ListPtr->BackPtr = ListPtr->BackPtr->Next;
printf("Adding %s\n", ListPtr->BackPtr->ElementPtr->ElementValue);
}
/* List size plus one */
(ListPtr->NumElements)++;
}
ElementStructs *RemoveFromFrontOfLinkedList(LinkedLists *ListPtr)
{
if(ListPtr->NumElements > 0)
{
ElementStructs *removedElement = ListPtr->FrontPtr->ElementPtr;
LinkedListNodes *removedNode = ListPtr->FrontPtr;
if(ListPtr->NumElements == 1)
{
ListPtr->FrontPtr = NULL;
}
else
{
ListPtr->FrontPtr = ListPtr->FrontPtr->Next;
ListPtr->FrontPtr->Previous = NULL;
}
(ListPtr->NumElements)--;
free(removedNode);
return removedElement;
}
else
{
ElementStructs *nullElement = NULL;
return nullElement;
}
}
ElementStructs *RemoveFromBackOfLinkedList(LinkedLists *ListPtr)
{
if(ListPtr->NumElements > 1)
{
ElementStructs *removedElement = ListPtr->BackPtr->ElementPtr;
LinkedListNodes *removedNode = ListPtr->BackPtr;
if(ListPtr->NumElements == 1)
{
ListPtr->BackPtr = NULL;
}
else
{
ListPtr->BackPtr = ListPtr->BackPtr->Previous;
ListPtr->BackPtr->Next = NULL;
}
(ListPtr->NumElements)--;
free(removedNode);
return removedElement;
}
else
{
ElementStructs *nullElement = NULL;
return nullElement;
}
}
void DestroyLinkedList(LinkedLists *ListPtr)
{
while(ListPtr->FrontPtr != NULL)
{
LinkedListNodes *removedNode = ListPtr->FrontPtr;
ListPtr->FrontPtr = ListPtr->FrontPtr->Next;
/* Deallocate element in node */
free(removedNode->ElementPtr->ElementValue);
free(removedNode->ElementPtr);
/* Deallocate node */
free(removedNode);
}
free(ListPtr);
}
TestList.c:
/******************************************************************************
* Tests the functionality of the LinkedList specified in LinkedLists.h using
* the file "american-english-words". Reads in individual words and stores them
* as node elements in the LinkedList.
* Written by xxxxx
*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "LinkedLists.h"
#define MAX_LENGTH 100 /* Length of longest word in any major English dictionary */
int main(int argc, char** args)
{
if(argc == 2)
{
/* Initialize LinkedList */
LinkedLists *LL;
/* File pointer to input file */
FILE *fp;
/* Node to store input data from file */
ElementStructs *NodeData;
/* Loop completion boolean */
int Done;
/* Loop position counter */
int Position;
/* Iterator */
ElementStructs *CurElement;
/* Open input file and check that it is readable. If not, exit */
fp = fopen(args[1], "r");
if(fp == NULL)
{
fprintf(stderr, "File open failed.");
return 2;
}
/* Initialize linked list and other necessary variables */
LL = malloc(sizeof(*LL));
InitLinkedList(LL);
Done = 0;
Position = 0;
do
{
if(!feof(fp))
{
/* Allocate space for new node data */
NodeData = malloc(sizeof(ElementStructs));
/* Allocate space in node element for input string */
NodeData->ElementValue = malloc(MAX_LENGTH * sizeof(char));
/* Read new node data from file */
fscanf(fp, "%s", NodeData->ElementValue);
/* Assign scanned values to node elements */
NodeData->ElementPosition = Position;
/*strcpy(NodeData->ElementValue, readString);*/
/* Add data node to LinkedList */
AddToFrontOfLinkedList(LL, NodeData);
}
else
Done = 1;
Position++;
}while(Done == 0);
do
{
CurElement = RemoveFromFrontOfLinkedList(LL);
if(CurElement != NULL)
printf("Word #%d: %s\n", CurElement->ElementPosition,
CurElement->ElementValue);
}while(CurElement != NULL);
/* Deallocate linked list */
DestroyLinkedList(LL);
fclose(fp);
}
/* Bad command line input */
else
{
fprintf(stderr, "Incorrect number of arguments");
return 1;
}
return 0;
}
The program compiles fine. However, running it causes a seg fault at the end of execution, and valgrind reports multiple memory leaks (Shown below). Please, if you have any help to offer, I would be extremely grateful. The issue is mostly in the RemoveFromBackOfLinkedList and RemoveFromFrontOfLinkedList methods in the LinkedList.c module. There's a block of code in the main module (TestList.c) that calls one of these functions (I've tried both, but they have nearly identical functionality, and neither works). The block is a do/while loop shown below:
do
{
CurElement = RemoveFromFrontOfLinkedList(LL);
if(CurElement != NULL)
printf("Word #%d: %s\n", CurElement->ElementPosition,
CurElement->ElementValue);
}while(CurElement != NULL);
Valgrind Results:
Word #225921: stoich
.
.
.
.
Word #6: Cam's
Word #5: petrochemistry's
Word #4: Tera
Word #3: benedictions
Word #2: wisted
Word #1: toxins
==4849== Invalid write of size 8
==4849== at 0x400B5C: RemoveFromFrontOfLinkedList (in /home/amb2189/hw3/TestList)
==4849== by 0x40085B: main (in /home/amb2189/hw3/TestList)
==4849== Address 0x10 is not stack'd, malloc'd or (recently) free'd
==4849==
==4849==
==4849== Process terminating with default action of signal 11 (SIGSEGV)
==4849== Access not within mapped region at address 0x10
==4849== at 0x400B5C: RemoveFromFrontOfLinkedList (in /home/amb2189/hw3/TestList)
==4849== by 0x40085B: main (in /home/amb2189/hw3/TestList)
==4849== If you believe this happened as a result of a stack
==4849== overflow in your program's main thread (unlikely but
==4849== possible), you can try to increase the size of the
==4849== main thread stack using the --main-stacksize= flag.
==4849== The main thread stack size used in this run was 8388608.
==4849==
==4849== HEAP SUMMARY:
==4849== in use at exit: 23,965,172 bytes in 413,185 blocks
==4849== total heap usage: 619,775 allocs, 206,590 frees, 28,923,332 bytes allocated
==4849==
==4849== 16 bytes in 1 blocks are possibly lost in loss record 1 of 9
==4849== at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4849== by 0x4007E2: main (in /home/amb2189/hw3/TestList)
==4849==
==4849== 200 bytes in 2 blocks are possibly lost in loss record 5 of 9
==4849== at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4849== by 0x4007F0: main (in /home/amb2189/hw3/TestList)
==4849==
==4849== 23,963,992 (3,305,392 direct, 20,658,600 indirect) bytes in 206,587 blocks are definitely lost in loss record 9 of 9
==4849== at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4849== by 0x4007E2: main (in /home/amb2189/hw3/TestList)
==4849==
==4849== LEAK SUMMARY:
==4849== definitely lost: 3,305,392 bytes in 206,587 blocks
==4849== indirectly lost: 20,658,600 bytes in 206,586 blocks
==4849== possibly lost: 216 bytes in 3 blocks
==4849== still reachable: 964 bytes in 9 blocks
==4849== suppressed: 0 bytes in 0 blocks
==4849== Reachable blocks (those to which a pointer was found) are not shown.
==4849== To see them, rerun with: --leak-check=full --show-reachable=yes
==4849==
==4849== For counts of detected and suppressed errors, rerun with: -v
==4849== Use --track-origins=yes to see where uninitialised values come from
==4849== ERROR SUMMARY: 5 errors from 5 contexts (suppressed: 2 from 2)
Segmentation fault (core dumped)
==4769== 8 bytes in 1 blocks are definitely lost in loss record 1 of 4
==4769== at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4769== by 0x400B46: RemoveFromFrontOfLinkedList (in /home/amb2189/hw3/TestList)
==4769== by 0x400869: main (in /home/amb2189/hw3/TestList)
This refers to the following code:
LinkedListNodes *removedNode = malloc(sizeof(removedNode));
removedNode = ListPtr->FrontPtr;
You malloc a block of memory then assign it to removedNode then immediately overwrite the pointer value. This does not assign to the contents of removedNode, but it overwrites the pointer.
The next memory leak occurs in main as pointed out by Valgrind:
CurElement = malloc(sizeof(*CurElement));
The next usage of CurElement immediately overwrites the pointer value, which means you have lost your reference to the allocated memory:
CurElement = RemoveFromFrontOfLinkedList(LL);
Make some changes in your 2 delete functions,,
ElementStructs *RemoveFromFrontOfLinkedList(LinkedLists *ListPtr)
{
if(ListPtr->NumElements > 0)
{
/* ElementStructs *removedElement = ListPtr->FrontPtr->ElementPtr;*/
/*LinkedListNodes *removedNode = malloc(sizeof(removedNode)); // no need allocate because you are deleting the node
removedNode = ListPtr->FrontPtr;*/
LinkedListNodes *removedNode = ListPtr->FrontPtr; // use this line
ListPtr->FrontPtr = ListPtr->FrontPtr->Next;
ListPtr->FrontPtr->Previous = NULL;
free(removedNode);
(ListPtr->NumElements)--;
return NULL;
}
else
{
ElementStructs *nullElement = NULL;
return nullElement;
}
}
ElementStructs *RemoveFromBackOfLinkedList(LinkedLists *ListPtr)
{
if(ListPtr->NumElements != 0)
{
ElementStructs *removedElement = ListPtr->BackPtr->ElementPtr;
LinkedListNodes *removedNode = ListPtr->BackPtr;
ListPtr->BackPtr = ListPtr->BackPtr->Previous;
ListPtr->BackPtr->Next = NULL;
free(removedNode);
(ListPtr->NumElements)--; // add this line
return removedElement;
}
else
{
ElementStructs *nullElement = NULL;
return nullElement;
}
}
EDIT 1
CurElement = malloc(sizeof(*CurElement)); this is another mem-leak in main, you don't have to use malloc for it, just remove this line.
EDIT 2: Why aren't you freeing your ELEMENT and ELEMENTVALUE...?
do
{
CurElement = RemoveFromFrontOfLinkedList(LL);
if(CurElement != NULL) {
printf("Word #%d: %s\n", CurElement->ElementPosition,
CurElement->ElementValue);
free(CurElement->ElementValue); //add these 2 line to free ELEMENTVALUE And ELEMENT.
free(CurElement);
}
}while(CurElement != NULL);
I think one leak is from CurElement = malloc(sizeof(*CurElement));. The next use of CurElement over-writes this pointer.
Its also not immediately clear where NodeData->ElementValue gets freed.
what do you want to do with:
LinkedListNodes *removedNode = malloc(sizeof(removedNode));
removedNode = ListPtr->FrontPtr;
you malloc some space and overwride the pointer