I have a small bug somewhere, was hoping someone with a little more C knowledge than I have to run through it real quick.
Im not certain if something is wrong in the map_put method or the map_get method, but I can only seem to map_get the first object in my list!?
anyhelp would be appreciated! thanks!
//----------------main---------------
#include <assert.h>
#include <stdio.h>
#include "map.h"
#include "map.c"
int main(){
map_t* newList = malloc(sizeof(map_t));
const char* passString ="a";
const char* secondString="2";
map_put(newList,"1","45");
map_put(newList,"3","3");
map_put(newList,"7","34");
map_put(newList,"a","45");
map_put(newList,"f","45");
map_put(newList,"2","45");
map_put(newList,passString,secondString);
map_get(newList, "3");
//printf("%d\n", map_size(newList));
printf("%1s\n", map_get(newList, "3"));
printf("%1s\n", map_get(newList, "1"));
printf("%1s\n", map_get(newList, "2"));
}
-----------------map.c----------------------
#include <assert.h>
#include "map.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
typedef int bool;
enum { false, true };
int size;
void map_init(map_t* self)
{
if(self==NULL)
self = (map_t*)malloc(sizeof(map_t));
self->entry = NULL;
self->size = 0;
}
int map_put(map_t* self, const char* key, const char* val)
{
assert(self != NULL);
//Create The Node Here For Storing Info;
struct _map_entry *node = (struct _map_entry *)malloc(sizeof(struct _map_entry));
node->key = (char *)key;
node->value = (char *)val;
node->next = NULL;
if(self==NULL)
{
map_init(self);
self->entry = node;
self->size = 1;
}
else
{
struct _map_entry *TempNode = self->entry;
if(TempNode==NULL)
{
TempNode = node;
self->entry = TempNode;
self->size = 1;
}
else
{
bool KeyExist = false;
while(TempNode->next != NULL)
{
if(strcmp(TempNode->key,node->key)==0)
{
KeyExist = true;
TempNode->value = node->value;
break;
}
TempNode = TempNode->next;
}
if(KeyExist)
{
return "Already Exists";
}
TempNode ->next = node;
self->size = self->size + 1;
}
}
}
const char* map_get(map_t* self, const char* key)
{
assert(self != NULL);
if(self==NULL)
return "";
struct _map_entry *StartNode = self->entry;
while(StartNode != NULL)
{
if(strcmp(StartNode->key,key)==0){
return StartNode->value;
}
else
StartNode = StartNode->next;
}
return "";
}
int map_size(map_t* self)
{
assert(self != NULL);
if(self==NULL)
return 0;
else
return size;
}
int map_remove(map_t* self, const char* key)
{
assert(self != NULL);
if(self==NULL)
return 0;
int totalRemovedNode = 0;
struct _map_entry *StartNode = self->entry;
struct _map_entry *TempNode = NULL;
while(StartNode != NULL)
{
if(strcmp(StartNode->key,key)==0)
{
struct _map_entry *Node = StartNode->next;
free(StartNode);
StartNode = TempNode;
TempNode = StartNode;
TempNode->next = Node;
size = size - 1;
totalRemovedNode = totalRemovedNode + 1;
}
StartNode = StartNode->next;
size = size - totalRemovedNode;
}
return totalRemovedNode;
}
void map_destroy(map_t* self)
{
assert(self != NULL);
struct _map_entry *StartNode = self->entry;
struct _map_entry *TempNode = NULL;
while(StartNode != NULL)
{
TempNode = StartNode->next;
free(StartNode);
StartNode = TempNode;
}
self->size = 0;
self->entry = NULL;
free(self);
}
int map_deserialize(map_t* self, FILE* stream)
{
assert(self != NULL);
assert(stream != NULL);
self->entry = NULL;
if(stream == NULL)
{
return 0;
// error
} else {
char *line = malloc(1024);
while(fgets(line,1024,stream))
{
char *value = strstr(line,":");
int keylength = value - line;
char *key = malloc(sizeof(keylength));
int i;
for(i = 0; i < keylength; i++)
{
key[i] = line[i];
}
key[keylength] = '\0';
value++;
map_put(self,key,value);
}
}
fclose(stream);
return self->size;
}
int map_serialize(map_t* self, FILE* stream)
{
assert(self != NULL);
assert(stream != NULL);
if(stream == NULL)
{
return 0;
}
struct _map_entry *it = self->entry;
while (it != NULL)
{
fwrite (it->key, sizeof (it->key), 1, stream);
fwrite (":", 1, 1, stream);
fwrite (it->value, sizeof (it->value), 1, stream);
fwrite ("\r\n", 2, 1, stream);
it = it->next;
}
return self->size;
}
----------------------map.h---------------------------
#ifndef __A1_MAP_H__
#define __A1_MAP_H__
#include <stdio.h>
typedef struct _map_entry map_entry_t;
struct _map_entry {
char* key;
char* value;
map_entry_t* next;
};
typedef struct _map {
map_entry_t* entry;
int size;
} map_t;
// Part one functions.
void map_init(map_t*);
int map_put(map_t*, const char*, const char*);
const char* map_get(map_t*, const char*);
int map_remove(map_t*, const char*);
int map_size(map_t*);
void map_destroy(map_t*);
// Part two functions.
int map_serialize(map_t*, FILE*);
int map_deserialize(map_t*, FILE*);
#endif
---------------------------
General Notes
According to 9899:2011 section 7.1.3 Reserved Identifiers, "all identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use". The same section of the C standard also says "All identifiers that begin with an underscore are always reserved for use as identifiers with file scope in both the ordinary and tag name spaces".
Please note that "reserved" means "you aren't allowed to do it". This means that when you #define __A1_MAP_H__, you are treading on namespace that doesn't belong to you, which is always considered to be inadvisable. Similarly, your struct _map_entry and your struct _map are also treading on reserved namespace. Consider renaming all these symbols.
You should not be casting the value that is returned by the malloc() function. You do this, inconsistently.
You should not be using the assert() macro for normal error checking. The assert() function is for debug-level sanity checks during the development process, that can be safely excluded from the code in production builds.
map_init()
Your map_init() function is wrong. If you pass a NULL to the map_init() function it allocates storage for one map_t, and initializes it, but doesn't have any way of returning it to the caller, so that storage is leaked (lost) as soon as the function returns, and the caller doesn't get anything. You should rewrite this function either to require a valid object be passed to it, or to allow it to return a pointer to a newly allocated object.
map_put()
You declare and define the map_put() function as returning a value of type int, but the only code path that returns anything at all, returns a char *.Additionally, this function doesn't copy the strings that are passed to it, but you're passing it both string literals and strings that are allocated using malloc().
map_get()
HINT: What does this function return if the first node is a match? If the second node is a match? If no node is a match?
map_size()
Why is the function returning the value of a file-scope variable that is never properly initialized? Did you perhaps intend, instead, for it to return the value of one of the elements of the map_t structure?
map_remove()
This function is so seriously messed up, that all I can advise you to do is go back to whatever reference you're learning about linked lists from, and re-read it.
map_destroy()
You aren't free()ing the strings attached to each node. You can't, of course, because you aren't copying them in, in map_put(), but some of what you put in there was allocated with malloc(), so you're leaking memory here.
map_deserialize()
This function does an odd half-init of the map_t that's passed to it, and it does it before validating prerequisites.
You allocate storage for line only once, using malloc(), then proceed to pass pointers into the middle of that storage to map_put(), which just stores the pointers instead of copying them, so you're overwriting the value every time you read the next line.
You malloc() storage for key each time through the loop, but you're allocating one byte fewer than you need, so you're constantly writing into storage that you don't own, every time you null-terminate that key you've just copied.
map_serialize()
HINT: If you have a char *foo, what is sizeof(foo)? How does this differ from strlen(foo)?
Related
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]);
I cannot figure out how to run this correctly, gives segmentation error. A piece of code is below. Can you look at head too , i am not sure if it is right way of initialising head to null in another file , it is run as follows :
Table tb ;
tb= initialise_table (table_size);
tb = insert(text_words,tb);
//these 3 typedef declarations are in a "some.h" file
typedef struct node * tree_ptr;
typedef char* Key_Type;
typedef struct table* Table;
struct node {
Key_Type element;
tree_ptr left;
tree_ptr right;
};
struct table {
tree_ptr head;
};
Table init_table() {
Table head = NULL;
}
Table insert(Key_Type key ,Table temp ) {
tree_ptr t = (tree_ptr)malloc(sizeof(tree_ptr));
t->element = key;
// t->left = t->right = NULL;
if (temp->head==NULL) {
temp = (Table)malloc (sizeof (Table));
temp->head = t;
printf("empty tree ");
}
else {
temp = insert(t->element,temp);
printf("inserted into ");
}
return temp;
printf("wowo!");
}
The primary issue is in the code which, you say, is used to invoke the functions:
Table tb;
tb = insert(text_words, tb);
You have an uninitialized pointer, tb, which you pass to the function. Inside the function, you have:
Table insert(Key_Type key, Table temp)
{
tree_ptr t = (tree_ptr)malloc(sizeof(*t)); // Fixed size
t->element = key;
// t->left = t->right = NULL;
if (temp->head==NULL)
{
You're therefore accessing (dereferencing) the undefined pointer, and your program is crashing.
You should, I assume, be initializing your table with table_init(), but that function is actually no help whatsoever. It defines and initializes a local variable, but doesn't return anything even though it promises to do so.
Please see Is it a good idea to typedef pointers? The short answer is 'No, it usually isn't a good idea'.
You still have problems even if you fix the calling code like this (a necessary but not sufficient step):
Table tb = NULL;
tb = insert(text_words, tb);
or maybe:
Table tb = init_table();
tb = insert(text_words, tb);
but you need a seriously upgraded version of init_table(), such as:
Table init_table(void)
{
Table root = malloc(sizeof(*head));
root->head = NULL;
return root;
}
Your code in insert() needs to ensure that it does not dereference a null pointer (instead of an indeterminate pointer).
Table insert(Key_Type key, Table root)
{
tree_ptr t = (tree_ptr)malloc(sizeof(*t)); // Fixed size
t->element = key;
t->left = t->right = NULL;
if (root == NULL)
{
root = init_table();
root->head = t;
}
else
{
…
}
return root;
}
Given the Key_Type is a char * in disguise, you may need to review how you save the keys in the tree structure; you may need to use strdup() to copy the data. It is impossible to say for sure without seeing how you are managing the strings that you pass to the insert() function. It could be OK to just save the pointer if the calling code ensures that a new pointer is passed each time. OTOH, if the same pointer is passed each time, you definitely need to copy the data, and using strdup() is a sensible way of doing that. Note that strdup() is standard on POSIX; it is not part of standard C.
Here's one major problem:
tree_ptr t = (tree_ptr) malloc(sizeof(tree_ptr));
should be:
tree_ptr t = (tree_ptr) malloc(sizeof(struct node));
Your code doesn't actually do any binary search. Indeed, it just infinitely recurses creating new nodes. Try something more like this:
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
typedef struct Node
{
char *element;
struct Node *left;
struct Node *right;
} Node;
typedef struct
{
Node *root;
size_t size;
} Tree;
void Tree_init(Tree *t);
Node *Tree_insert(Tree *t, const char *key);
void Tree_insert_r(Node *subtree, Node *n, size_t size);
void Tree_pre_order_r(Node *subtree);
void Tree_init(Tree *t)
{
t->root = NULL;
t->size = 0;
}
Node *Tree_insert(Tree *t, const char *key)
{
Node *ret = (Node*) malloc(sizeof(Node));
if (ret)
{
ret->left = ret->right = NULL;
if ((ret->element = strdup(key))) /* make a copy of key */
{
if (NULL != t->root)
Tree_insert_r(t->root, ret, t->size);
else
t->root = ret;
++t->size;
}
else
{
free(ret);
ret = NULL;
}
}
return ret;
}
void Tree_insert_r(Node *subtree, Node *n, size_t size)
{
int cmp = strcmp(n->element, subtree->element);
if (cmp < 0 || (cmp == 0 && size % 2 == 0))
{
if (NULL != subtree->left)
subtree = subtree->left;
else
{
subtree->left = n;
return;
}
}
else
{
if (NULL != subtree->right)
subtree = subtree->right;
else
{
subtree->right = n;
return;
}
}
Tree_insert_r(subtree, n, size);
}
void Tree_pre_order_r(Node *subtree)
{
if (NULL == subtree)
return;
fprintf(stdout, "'%s'\n", subtree->element);
Tree_pre_order_r(subtree->left);
Tree_pre_order_r(subtree->right);
}
int main()
{
Tree t;
Tree_init(&t);
Tree_insert(&t, "Hello");
Tree_insert(&t, "World!");
Tree_insert(&t, "etc.");
Tree_pre_order(t.root);
return 0;
}
I have a program that takes in strings using the command line prompts argv and argc. I keep getting a segmentation fault when I go to run the code and after much researching, I cannot determine what might be causing this. Maybe how I execute the code is the issue? I am using gcc -o code code.c then ./code one two three with one two three being the strings added to the linked list. Any assistance in determining where my error might be would be great.
Here is my code:
#include <stdio.h>
#include <stdlib.h>
typedef struct list_node_s{
char the_char;
struct list_node_s *next_node;
}list_node;
void insert_node(list_node *the_head, char the_char);
void print_list(list_node *the_head);
int main(int argc, char *argv[]){
char next_char;
list_node *the_head = NULL;
insert_node(the_head, next_char);
the_head->next_node = malloc(sizeof(list_node));
if(the_head == NULL){
return 1;
}
the_head->the_char = 1;
the_head->next_node == NULL;
int the_count, the_count2;
for(the_count = 0; the_count < sizeof(argv); the_count++){
for(the_count2 = 0; argv[the_count][the_count2] != '\0'; the_count2++){
next_char = argv[the_count][the_count2];
insert_node(the_head, next_char);
}
}
print_list(the_head);
return (0);
}
void insert_node(list_node *the_head, char the_char){
list_node * current_node = the_head;
while (current_node->next_node != NULL) {
current_node = current_node->next_node;
}
current_node->next_node = malloc(sizeof(list_node));
current_node->next_node->the_char = the_char;
current_node->next_node->next_node = NULL;
}
void print_list(list_node *the_head){
if(the_head == NULL){
printf("\n");
}else{
printf("%c", the_head->the_char);
print_list(the_head->next_node);
}
}
Change this:
list_node *the_head = NULL;
insert_node(the_head, next_char);
the_head->next_node = malloc(sizeof(list_node));
to:
list_node the_head = { '\0', NULL };
to initialize the_head to an empty node.
One problem is in this function:
void insert_node(list_node *the_head, char the_char){
list_node * current_node = the_head;
while (current_node->next_node != NULL) {
current_node = current_node->next_node;
}
current_node->next_node = malloc(sizeof(list_node));
current_node->next_node->the_char = the_char;
current_node->next_node->next_node = NULL;
}
When you call it in main you're basically passing in NULL because you're setting the_head to NULL. You're trying to access current_node->next_node in the while loop conditions, but because of what you're passing in, you're basically doing NULL->next_node.
You need to initialize your head to an empty list_node. Basically since you're using a char as your node element you could set the value of the char to 0x00, which would make it a zero byte. Then that way you know that when you're at that value, you're at the head.
I don't mean to self-promote, but if you want to look at some code for this have a look at this github repo for the Barry_CS-331 Data Structures class. There's C and C++ in there for the Data Structures. I think it might have a list but if not you can use the stack and the queue as an overall example.
I have modified you code, there has some bugs:
1)、the key bug is in this code.
for(the_count = 0; the_count < sizeof(argv); the_count++)
{
for(the_count2 = 0; argv[the_count][the_count2] != '\0'; the_count2++)
{
next_char = argv[the_count][the_count2];
insert_node(the_head, next_char);
}
}
there some bugs:
you cann't use the_count < sizeof(argv), because of the type of argv is char* []; so sizeof(argv) maybe 4 or 8, based on your os.
the right is:
for(the_count = 1; the_count < argc; the_count++){
for(the_count2 = 0; argv[the_count][the_count2] != '\0'; the_count2++){
next_char = argv[the_count][the_count2];
insert_node(the_head, next_char);
}
}
2、this code aose has some bugs:
list_node *the_head = NULL;
insert_node(the_head, next_char);
the_head->next_node = malloc(sizeof(list_node));
if(the_head == NULL){
return 1;
}
the_head->the_char = 1;
the_head->next_node == NULL;
insert_node(the_head, next_char); is no need, you'd better do the_head->the_char = '\0', because of char 1 is no printable character.
One way:
#include <stdio.h>
#include <stdlib.h>
typedef struct list_node_s{
char the_char;
struct list_node_s *next_node;
}list_node;
void insert_node(list_node *the_head, char the_char);
void print_list(list_node *the_head);
int main(int argc, char *argv[]){
list_node *the_head = NULL;
int the_count, the_count2;
for(the_count = 0; the_count < argc; the_count++)
{
for(the_count2 = 0; the_count2 < strlen(argv[the_count]); the_count2++)
insert_node(&the_head, argv[the_count][the_count2]);
}
print_list(the_head);
return (0);
}
void insert_node(list_node **the_head, char the_char){
list_node *new_node;
list_node *tail_node;
/* Allocate and populate a new node. */
new_node = malloc(sizeof(list_node));
new_node->the_char = the_char;
new_node->next_node = NULL;
/* Is the_head already initialized? */
if(*the_head)
{
/* Yes... find the tail_node. */
tail_node = *the_head;
while(tail_node->next)
tail_node = tail_node->next;
/* Append the new_node to the end of the list. */
tail_node->next = new_node;
return;
}
/* the_head was not initialized. The new_node will be the head node. */
*the_head = new_node;
return;
}
void print_list(list_node *the_head){
if(the_head == NULL){
printf("\n");
}else{
printf("%c", the_head->the_char);
print_list(the_head->next_node);
}
}
Ok my issue is when I write the node it just overwrites the head node, so when you call map_get you only get one node with data. I want to hand the data on the head or push the head node and hang it front either works, whatever one is the easiest would be best thanks for your help.
#include <assert.h>
#include<stdlib.h>
#include "map.h"
#include <string.h>
int main(){
map_t* newList = malloc(sizeof(map_t));
map_init(newList);
const char* passString ="a";
const char* secondString="2";
map_put(newList,"3","3");
map_put(newList,"7","34");
map_put(newList,"a","45");
map_put(newList,passString,secondString);
map_get(newList,secondString);
}
void map_init(map_t* self) {
map_entry_t* newNode= malloc(sizeof(map_entry_t)); // allocate
self->size = 0;
self->entry = newNode; // link next
}
int map_put(map_t* self, const char* key, const char* val) {
assert(self != NULL);
map_entry_t* current;
//current = self->entry->key;
while(current->next != NULL){
current = current->next;
}
self->entry->key = key;
self->entry->value = val;
map_entry_t* newNode = malloc(sizeof(map_entry_t));
printf("\ntry printing the list \n");
printf(self->entry->key);
printf(" :was the key and the value is \n");
printf(self->entry->value);
printf("\n");
}
const char* map_get(map_t* self, const char* key) {
assert(self != NULL);
const char* target = key;
int i=0;
for(i; i<=self->size; i++)
{
printf("\n\ninside the list\n");
printf(self->entry->value);
printf("\n");
}
}
int map_size(map_t* self) {
assert(self != NULL);
}
int map_remove(map_t* self, const char* key) {
assert(self != NULL);
}
int map_serialize(map_t* self, FILE* stream) {
assert(self != NULL);
}
int map_deserialize(map_t* self, FILE* stream) {
assert(self != NULL);
}
void map_destroy(map_t* self) {
assert(self != NULL);
}
map.h
#ifndef __A1_MAP_H__
#define __A1_MAP_H__
#include <stdio.h>
#define SYS_ERROR -1
#define OK 0
#define KEY_EXISTS 1
#define NO_KEY_EXISTS 2
// Strange type definition due to the recursive nature
// of the struct. This technique is called 'forward
// declaration' and is necessary for compilation reasons.
typedef struct _map_entry map_entry_t;
struct _map_entry {
char* key;
char* value;
map_entry_t* next;
} ;
typedef struct _map {
map_entry_t* entry;
int size;
} map_t;
// Part one functions.
void map_init(map_t*);
int map_put(map_t*, const char*, const char*);
const char* map_get(map_t*, const char*);
int map_remove(map_t*, const char*);
int map_size(map_t*);
void map_destroy(map_t*);
// Part two functions.
int map_serialize(map_t*, FILE*);
int map_deserialize(map_t*, FILE*);
#endif
In map_put, you should initialize current to point at the head of the list self. Right now, it starts pointing ... somewhere ... in memory, and then you try to walk a list from there. You then completely ignore that and write directly into the head element of the list (self) instead of a new node (newNode). You'll also need to then set a link from current to your new node.
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?