C trie memory leak in add function - c

I've been trying to get my code to work. It compiles but when I run it I get a segfault and gdb and valgrind pointed to this line in particular and my problem is that I don't really know how to fix it:
if (!pCrawl->cvec[index]) {
which is in addword().
Essentially I'm supposed to implement functions in the head file for a trie data structure: makedictionary, add, search, and delete.
Here's the school supplied header file:
#define VECSIZE ('z'-'a' + 1)
typedef char *word;
enum __bool__ { FALSE, TRUE };
typedef enum __bool__ bool;
typedef struct __tnode__ *Dict, TNode;
struct __tnode__ {
Dict cvec[VECSIZE];
bool eow;
};
void newdict(Dict *dp);
void addword (const Dict r, const word w);
bool checkword (const Dict r, const word w);
void delword (const Dict r, const word w);
void barf(char *s);
Also not that since I can't change the header file, and bool is a typedef I can't use stdbool.h.
And this is my C code which I'm writing up:
#define CHAR_TO_INDEX(c) ((int)c - (int)'a')
void newdict (Dict *dp) {
*dp = NULL;
dp = (Dict *)malloc(sizeof(Dict));
if (dp) {
int i;
(*dp)->eow = FALSE;
for (i = 0; i < VECSIZE; i++) {
(*dp)->cvec[i] = NULL;
}
}
}
void addword (const Dict r, const word w) {
int level;
int length = strlen(w);
int index;
Dict pCrawl = r;
printf("line 1\n");
for (level = 0; level < length; level++) {
index = CHAR_TO_INDEX(w[level]);
if (!pCrawl->cvec[index]) {
newdict(&(pCrawl->cvec[index]));
}
pCrawl = pCrawl->cvec[index];
}
pCrawl->eow = TRUE;
}
bool checkword (const Dict r, const word w) {
int level;
int length = strlen(w);
int index;
Dict pCrawl = r;
for (level = 0; level < length; level++) {
index = CHAR_TO_INDEX(w[level]);
if (!pCrawl->cvec[index]) {
return FALSE;
}
pCrawl = pCrawl->cvec[index];
}
if (pCrawl != NULL && pCrawl->eow) {
return TRUE;
} else {
return FALSE;
}
}
I'm a bit new to C so any tips would be greatly appreciated. Thanks in advance.

I'm guessing that the confusion is in understanding
typedef struct _tnode__ *Dict, TNode;
This means that
Dict lookup;
is the same as
TNode* lookup;
So when you are creating the dictionary
void newdict(Dict* dp)
Is the same as
void newdict(TNode** dp)
So when allocating, allocating sizeof(Dict) is the same as sizeof(TNode*) i.e. sizeof a pointer. What is really required is a TNode. Note - in C there is no need to cast mallocs. It is only required for C++.
*dp = malloc (sizeof(TNode));
Try that and see if it fixes your problem.

Related

Inserting an item to dynamic dictionary is not working

As a learning exercise, I'm trying to implement my own dictionary in C. My dictionary type definition is:
#define MAXCAP 24
static size_t primes[MAXCAP + 2] = {
1,
101, 251, 509, 1021, 2039, 4093, 8191,
16381, 32749, 65521, 131071, 262139, 524287, 1048573,
2097143, 4194301, 8388593, 16777213, 33554393, 67108859, 134217689,
268435399, 536870909, 10737441789, 2147483647};
typedef struct item_tag item;
struct item_tag {
void *key;
void *val;
item *next;
};
typedef struct dict_tag {
size_t cap; // capacity of the dict, which is used as an index for primes[]
size_t size; // number of slots taken up out of the capacity of the dict
item **items;
int (*eq) (const void *, const void *);
int (*hash) (const void *, size_t n);
} dict;
My function for inserting a new entry to the dict is:
int dict_put(void *key, void *val, dict *d) {
int i;
item *kv;
if (!dict_get(key, d)) {
kv = malloc(sizeof(item));
kv->key = key;
kv->val = val;
kv->next = d->items[(i = d->hash(key, primes[d->cap]))];
d->items[i] = kv;
if (!kv->next)
d->size++;
if (d->size >= primes[d->cap] / 2)
expand(d);
return 1;
}
return 0;
}
Insertion works fine if I do not try to resize the dict using expand function which is defined as:
static void expand(dict *d) {
int i;
item *kv;
dict *tmp;
if (d->cap < MAXCAP) {
tmp = malloc(sizeof(dict));
init(d->cap + 1, d->hash, d->eq, tmp);
for (i = 0; i < d->cap; i++) {
for (kv = d->items[i]; kv; kv = kv->next)
dict_put(kv->key, kv->val, tmp);
}
destroy_items(0, d);
d->cap = tmp->cap;
d->size = tmp->size;
d->items = tmp->items; // looks like there are no items in dict after this step
free(tmp);
} else
fprintf(stderr, "dict: max size reached.\n");
}
In the above function, I'm trying to create a new temporary larger dict and then copy the pointer to the new list of items to the old dict. The init function is:
static void init(size_t n, const int (*hash) (const void *, size_t n), const int (*eq) (const void *, const void *),
dict *d) {
d->cap = n;
d->size = 0;
d->eq = eq;
d->hash = hash;
d->items = calloc(primes[d->cap], sizeof(item *));
}
Your problem is on this line in the expand function
for (i = 0; i < d->cap; i++) {
should be changed to
for (i = 0; i < primes[d->cap]; i++) {

Implementing a generic arraylist in C [duplicate]

This question already has an answer here:
Making a generic ArrayLIst in C
(1 answer)
Closed 2 years ago.
I am trying to implement a java-like arraylist in C and now I am trying to make it generic. I am new to C and pointer arithmetic but I tried using void* pointer for container, then element size(for size of data type), capacity and size. I am trying to debug my code and it is giving a random value on the screen.
I cant seem to find what is wrong with my add method ? and how should I go about the printlist method as we are not sure about the data type. I think my printLIst is wrong.
I am new to C and playing around for educational purposes and any help is appreciated. Thank you
____________________________________________________________________
#ifndef ARRAYLIST_H
#define ARRAYLIST_H
typedef struct ArrayList ArrayList;
typedef int bool;
#define false 0
#define true 1
struct ArrayList {
void *con;
int elementSize;
int numElements;
int conSize;
};
ArrayList *createArrayList(int );
int remove(ArrayList *, int);
void freeArrayList(ArrayList *);
int add(ArrayList *, void*);
void *getAtIndex(ArrayList *, int);
void printList(ArrayList *);
void resize(ArrayList *);
bool isEmpty(ArrayList*);
int getNumElements(ArrayList*);
int getConSize(ArrayList*);
#endif
_______________________________________________________________________
#include<stdio.h>
#include<stdlib.h>
#include"Consts.h"
#include<memory.h>
#include "ArrayList.h"
#define CAPACITY 5
#define EXTRA 100
ArrayList *createArrayList(int elementSize) {
ArrayList *arrayList = malloc(sizeof(ArrayList));
arrayList->elementSize = elementSize;
arrayList->conSize = CAPACITY;
arrayList->numElements = 0;
arrayList->con = malloc(elementSize * CAPACITY);
return arrayList;
}
void freeArrayList(ArrayList * arrayList) {
if (arrayList == NULL) {
return;
}else {
free(arrayList->con);
free(arrayList);
}
}
int add(ArrayList *list, void *input) {
if (list != NULL && input != NULL) {
if (list->numElements >= list->conSize) {
resize(list);
printf("resized\n");
}
list->con = input;
memcpy((char*)list->con + (list->numElements*list->elementSize), input, list->elementSize);
list->numElements++;
return 1;
}
return -1;
}
void resize(ArrayList *list) {
void *temp = realloc(list->con, (list->conSize + EXTRA) * list->elementSize);
if (temp != NULL) {
list->conSize += 100;
list->con = temp;
}
}
int remove(ArrayList * list, int i) {
if (list != NULL) {
//find index of value to remove
int elementSize = list->elementSize;
int lenToMove = elementSize * (list->numElements - (i + 1));
memmove((char *)list->con + i*elementSize, (i+1)*elementSize, lenToMove);
list->numElements--;
return 1;
}
return -1;
}
void printList(ArrayList *list) {
if (list != NULL) {
char *p = list->con;
for (int i = 0; i < list->numElements; i++) {
void* val = getAtIndex(list, i);
printf("%d \n", val);
}
}
}
void *getAtIndex(ArrayList *listptr, int index) {
return (char*)listptr->con + index * (listptr->elementSize);
}
int getNumElements(ArrayList * list) {
return list->numElements;
}
int getConSize(ArrayList * list) {
return list->conSize;
}
bool isEmpty(ArrayList * list) {
return list->numElements == 0;
}
___________________________________________________
#include<stdio.h>
#include<stdlib.h>
#include"ArrayList.h"
#include "Consts.h"
#pragma warning(disable : 4996)
int main() {
ArrayList * list = createArrayList(sizeof(int));
int x = 5;
add(list, &x);
printf("%d",(int *)getAtIndex(list, 0));
freeArrayList(list);
system("pause");
return 0;
}
Pay attention to the comments above. I know you're new to the site, but it's worth while getting to know how it works.
Anyway the problem is in this line of code:
printf("%d",(int *)getAtIndex(list, 0));
getAtIndex() returns a pointer so you need to dereference it to get the value. It should be:
printf("%d",*(int *)getAtIndex(list, 0));

Generic ArrayList in C

I have made a Java like ArrayList class in C for educational purposes however currently it is only good for integers. I want to make it Generic so it can take any type of data. How do I go about it. I read somewhere about creating a typedef void pointer. Any thoughts ?
........................................................................................................
Here is My code
#ifndef ARRAYLIST_H
#define ARRAYLIST_H
typedef struct ArrayList ArrayList;
typedef int bool;
#define false 0
#define true 1
struct ArrayList {
int *con;
int numElements;
int conSize;
};
ArrayList *createArrayList();
void freeArrayList(ArrayList *);
void add(ArrayList *, int);
void printList(ArrayList *);
void resize(ArrayList *);
int remove(ArrayList *, int);
bool isEmpty(ArrayList*);
int getNumElements(ArrayList*);
int getConSize(ArrayList*);
#endif
_____________________________________
#include<stdio.h>
#include<stdlib.h>
#include"Consts.h"
#include "ArrayList.h"
#define CAPACITY 5
ArrayList *createArrayList() {
ArrayList *arrayList = malloc(sizeof(ArrayList));
arrayList->conSize = CAPACITY;
arrayList->numElements = 0;
arrayList->con = malloc(sizeof(int) * CAPACITY);
return arrayList;
}
void freeArrayList(ArrayList * arrayList) {
if (arrayList == NULL) {
return;
}else {
free(arrayList->con);
free(arrayList);
}
}
void add(ArrayList *arrayList, int input) {
//printf("Num elements in add method before adding %d \n", arrayList->numElements);
if (arrayList->numElements >= arrayList->conSize) {
resize(arrayList);
printf("resized\n");
}
int size = arrayList->numElements;
//add element to the last
arrayList->con[size] = input;
arrayList->numElements = arrayList->numElements + 1
}
void resize(ArrayList *arrayList) {
int num = arrayList->numElements;
int oldSize = arrayList->conSize;
int newSize = oldSize + 50;
int *temp = realloc(arrayList->con, sizeof(type) * newSize);
if (temp != NULL) {
arrayList->con = temp;
arrayList->conSize = newSize;
}
}
int remove(ArrayList * arrayList, int val) {
int i = 0;
while (arrayList->con[i] != val) {
i++;
}
//remove this index
if (i == arrayList->conSize) {
return -1;
}
else {
int removedVal = arrayList->con[i]; int j;
for (j = i; j < arrayList->numElements ; j++) {
arrayList->con[j] = arrayList->con[j + 1];
}
arrayList->con[j + 1] = NULL;
arrayList->numElements = arrayList->numElements - 1;
return removedVal;
}
}
If you want the array list be able to store any type of data, you need to
1) make con a void pointer. void *con;
2) store additional metadata in the struct about the memory alignment of the type
3) add one more parameter to the constructor of array list, which is the additional metadata mentioned in 2)
4) when allocating memory, use the stored metadata instead of sizeof(whatever), like temp=malloc(stored_metadata_about_type*50);
Also notice that it is not usually a good idea to hardcode 50, and better declare it as a constant like buffer_size.

Pointer disappeared after passing to some functions (even with malloc)

My problem is that I created a pointer at the bottom Main.
Main will call load() to read input from a dictionary and insert the words in a inputfile into a TRIE *dict by calling insert() and getnode(). However, after load() return true, the *dict lost all of the value and I cannot get what I expected(i.e. showing cat is present as it is in my dictionary input file).
I have read from other websites that pointers can retain its value after doing malloc. So I have malloced for the *dict. Please kindly let me know why the value disappeared.
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#define CHAR_TO_INDEX(c) ((int)c - (int)'a')
#define ARRAY_SIZE(a) sizeof(a)/sizeof(a[0])
struct dict
{
char words[46];
struct dict* dictPath[26];
bool isEndOfWord;
};
// Returns new trie node (initialized to NULLs)
struct dict *getNode(void)
{
struct dict *pNode = NULL;
pNode = (struct dict *)malloc(sizeof(struct dict));
if (pNode)
{
int i;
for (i = 0; i < 26; i++)
pNode->dictPath[i] = NULL;
}
return pNode;
}
void insert(struct dict *root, const char *key)
{
int level;
int length = strlen(key);
int index;
struct dict *pCrawl = root;
for (level = 0; level < length; level++)
{
index = CHAR_TO_INDEX(key[level]);
if (!pCrawl->dictPath[index])
{
pCrawl = malloc(sizeof(struct dict));
pCrawl->dictPath[index] = getNode();
}
printf("%i\n",index);
pCrawl = pCrawl->dictPath[index];
}
// mark last node as leaf
pCrawl->isEndOfWord = true;
}
// Returns true if key presents in trie, else false
bool search(struct dict *root, const char *key)
{
int level;
int length = strlen(key);
int index;
struct dict *pCrawl = root;
for (level = 0; level < length; level++)
{
index = CHAR_TO_INDEX(key[level]);
if (!pCrawl->dictPath[index])
{
return false;
}
pCrawl = pCrawl->dictPath[index];
}
return (pCrawl != NULL && pCrawl->isEndOfWord);
}
bool load(struct dict *root, char *inputfile){
// open the dictionary file
FILE *infile = fopen(inputfile,"r");
int dictchar;
char tmpword[46];
int cnt = 0;
root = getNode();
// start iterating to read char
do
{
// read the character
dictchar = fgetc(infile);
if (dictchar != '\n')
{
// assign the dictionary character to a tmpword. tmpword will be used to fit into TRIES later
tmpword[cnt] = dictchar;
cnt ++;
}
// if the character is '\n', fit tmpword into TRIES
else
{
tmpword[cnt] = '\0';
cnt = 0;
for (int i = 0; i < ARRAY_SIZE(tmpword); i++)
insert(root, tmpword);
}
} while (dictchar != EOF);
return true;
}
int main (int argc, char *argv[]){
if (argc != 2)
{
return 1;
}
struct dict *root = malloc(sizeof(struct dict));
load(root, argv[1]);
char output[][32] = {"Not present in trie", "Present in trie"};
printf("%s --- %s\n", "cat", output[search(root, "cat")] );
return 0;
}
p.s. I took reference from https://www.geeksforgeeks.org/trie-insert-and-search/
In you function,
bool load(struct dict *root, char *inputfile)
you pass a root pointer, but then replace it with the result of getNode.
The calling code will not see this change.
You need to pass a pointer to the root pointer,
bool load(struct dict **root, char *inputfile)
for the calling code to see the change.
More simply, since you throw away the root with
root = getNode();
right near the top of the function, you could change the load signature:
struct dict * load(char *inputfile)
Instead of return true; at the end, return root; instead.
You don't have a path returning flase anyway.
Change the calling code too.
Instead of
struct dict *root = malloc(sizeof(struct dict));
load(root, argv[1]);
try this:
struct dict *root = load(argv[1]);

Heap error in DLL

I'm looking for some help with some C dll programming. I am getting an error in Visual Studio that occurs when I call free from within the dll. The program runs fine in debug mode within the IDE, but when I try to execute it as "Start without debugging", the program crashes. I read that with debugging, the heap is shared, which probably explains why the code runs fine with F5 and not Ctrl-F5. Is this correct??
I've searched around, and I learned that it is dangerous to pass dynamically allocated memory through the dll-exe boundary, however as I am calling malloc and free from within the same dll, this should not be a problem. I have posted the code below. Any help would be greatly appreciated. Please note that this code works on my Linux box, I am simply trying to port it to Windows to garner experience programming in Windows.
Thank you
#ifndef HASHTABLE_H
#define HASHTABLE_H
// The following ifdef block is the standard way of creating macros which make exporting
// from a DLL simpler. All files within this DLL are compiled with the HASHTABLE_EXPORTS
// symbol defined on the command line. This symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see
// HASHTABLE_API functions as being imported from a DLL, whereas this DLL sees symbols
// defined with this macro as being exported.
#ifdef HASHTABLE_EXPORTS
#define HASHTABLE_API __declspec(dllexport)
#else
#define HASHTABLE_API __declspec(dllimport)
#endif
#ifdef __cplusplus
extern "C" {
#endif
/**
* This is a naive implementation
* of a hashtable
*/
typedef struct node {
struct node *next;
char *value;
char *key;
} Node;
typedef struct hashtable {
struct node **nodes;
int num_elements;
int size;
int (*hash_function)(const char * const);
} Hashtable_str;
// Construction and destruction
HASHTABLE_API void tbl_construct(Hashtable_str **table);
HASHTABLE_API void tbl_destruct(Hashtable_str *table);
// Operations
HASHTABLE_API int tbl_insert (Hashtable_str *table, const char * const key, const char * const element); // return the key
HASHTABLE_API int tbl_remove(Hashtable_str *table, const char * const key);
HASHTABLE_API char * tbl_find(Hashtable_str *table, const char * const key); // return the element
HASHTABLE_API int size(Hashtable_str *table); // Return the size
// default hash function
int def_hash(const char * const key);
#ifdef __cplusplus
}
#endif
#endif
Here is the implementation code
// hashtable.cpp : Defines the exported functions for the DLL application.
//
#include "stdafx.h"
#include "hashtable.h"
#include <stdlib.h> // for memcpy
#include <stdio.h>
#include <string.h>
#define SIZE 100
int def_hash(const char * const key)
{
// simply sum the ascii
// values and take modulo
// 100
//int i;
//int sum;
//sum = 0;
//for (i=0; key[i] != '\0'; i++)
// sum += key[i];
//return sum % SIZE;
return 0;
}
// construct a hashtable and return a pointer
HASHTABLE_API void tbl_construct(Hashtable_str **tbl)
{
int i;
Hashtable_str *tbl_ptr;
*tbl = (Hashtable_str*) malloc (sizeof(Hashtable_str*));
tbl_ptr = *tbl;
tbl_ptr->nodes = (Node**) malloc (SIZE * sizeof(Node*));
for (i=0; i < SIZE; i++) tbl_ptr->nodes[i] = NULL;
tbl_ptr->hash_function = &def_hash;
tbl_ptr->num_elements = 0;
tbl_ptr->size = SIZE;
}
HASHTABLE_API void tbl_destruct(Hashtable_str *tbl)
{
void free_tbl_node(Node*); // declare the release function
int i;
for (i=0; i < tbl->size; i++)
{
if (tbl->nodes[i] != NULL)
free_tbl_node(tbl->nodes[i]);
}
}
void free_tbl_node(Node *curr_node)
{
if (curr_node->next != NULL)
free_tbl_node(curr_node->next);
free(curr_node->value);
curr_node->value = NULL;
free(curr_node->key);
curr_node->key = NULL;
//free(curr_node);
//Node *temp = NULL;
//Node *temp2 = NULL;
//temp = temp2 = curr_node;
//while (temp->next != NULL)
//{
// temp2=temp->next;
// free(temp->key);
// free(temp->value);
// free(temp);
// temp=temp2;
//}
}
// table operations
HASHTABLE_API int count(Hashtable_str *tbl) { return tbl->num_elements; }
HASHTABLE_API int size(Hashtable_str *tbl) { return tbl->size; }
HASHTABLE_API int tbl_insert(Hashtable_str *table, const char * const key, const char * const element)
{
int hash;
Node *temp_ptr = NULL;
hash = table->hash_function(key);
// printf("Placing into column %d\n", hash);
if (table->nodes[hash] == NULL)
{
table->nodes[hash] = (Node*) malloc(sizeof(Node*));
temp_ptr = table->nodes[hash];
temp_ptr->next = NULL;
temp_ptr->key = (char*) malloc (strlen(key) + 1 * sizeof(char));
temp_ptr->value = (char*) malloc (strlen(element) + 1 * sizeof(char));
strcpy_s(temp_ptr->key, strlen(key)+1, key);
strcpy_s(temp_ptr->value, strlen(element)+1, element);
table->num_elements += 1;
}
else
{
// Collision!!
temp_ptr = table->nodes[hash];
while (temp_ptr->next != NULL)
temp_ptr = temp_ptr->next;
temp_ptr->next = (Node*) malloc(sizeof(Node));
temp_ptr->next->key = (char*) malloc (strlen(key)+1 * sizeof(char));
temp_ptr->next->value = (char*) malloc (strlen(element)+1 * sizeof(char));
temp_ptr->next->next = NULL;
strcpy_s(temp_ptr->next->key, strlen(key)+1, key);
strcpy_s(temp_ptr->next->value, strlen(element)+1, element);
table->num_elements += 1;
}
// Return the hash value itself for hacking
return hash;
}
HASHTABLE_API int tbl_remove(Hashtable_str *tbl, const char * const key)
{
int hash;
Node *temp_ptr = NULL;
Node *chain = NULL;
hash = tbl->hash_function(key);
if (tbl->nodes[hash] == NULL)
return 1;
else
{
temp_ptr = tbl->nodes[hash];
if (strcmp(key, temp_ptr->key) == 0)
{
// The next node is the node in question
chain = temp_ptr->next;
free(temp_ptr->value);
printf("Deleted the value\n");
free(temp_ptr->key);
printf("Deleted the key\n");
//printf("About to delete the node itself\n");
//free(temp_ptr);
tbl->nodes[hash] = chain;
tbl->num_elements -= 1;
return 0;
}
else
{
while (temp_ptr->next != NULL)
{
if (strcmp(key, temp_ptr->next->key) == 0)
{
// The next node is the node in question
// So grab a pointer to the node after it
// and remove the next node
chain = temp_ptr->next->next;
free(temp_ptr->next->key);
free(temp_ptr->next->value);
//free(temp_ptr->next);
temp_ptr->next = chain;
tbl->num_elements -= 1;
return 0;
}
else
temp_ptr = temp_ptr->next;
}
}
// Couldn't find the node, so declare not existent
return 1;
}
}
HASHTABLE_API char * tbl_find(Hashtable_str *tbl, const char * const key)
{
// Compute the hash for the index
int hash;
Node *temp_ptr = NULL;
hash = tbl->hash_function(key);
if (tbl->nodes[hash] == NULL)
return NULL;
else
{
temp_ptr = tbl->nodes[hash];
if (strcmp(key, temp_ptr->key) != 0)
{
while (temp_ptr->next != NULL)
{
temp_ptr = temp_ptr->next;
if (strcmp(key, temp_ptr->key) == 0)
return temp_ptr->value;
}
}
// Couldn't find the node, so declare not existent
return NULL;
}
}
Here's my main
#include <hashtable.h>
#include <utils.h>
#include <stdio.h>
int main(int argc, char **argv)
{
int i=0;
Hashtable_str *my_table = NULL;
tbl_construct(&my_table);
tbl_insert(my_table, "Daniel", "Student");
tbl_insert(my_table, "Derek", "Lecturer");
//tbl_insert(my_table, "Melvyn", "Lecturer");
tbl_print(my_table);
printf("\nRemoving Daniel...\n");
tbl_remove(my_table, "Daniel");
//tbl_print(my_table);
tbl_destruct(my_table);
my_table = NULL;
scanf_s("%d", &i);
return 0;
}
This is incorrect as allocates the size of a pointer, not a Hashtable_str:
*tbl = (Hashtable_str*) malloc (sizeof(Hashtable_str*));
it should be:
*tbl = malloc(sizeof(Hashtable_str));
Same issue with:
table->nodes[hash] = (Node*) malloc(sizeof(Node*));
See Do I cast the result of malloc?

Resources