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

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]);

Related

Hashtable with linked list not work in c?

I've a problem with memory allocation for an hash table with linked list (for avoid collisions) in C.
I think that the problem is on allocation of an item.
I've made two scruct, one for the single item and one for the table.
The first have two pointer to next and prev item.
Please help me.
I stay on this code until 3 days.
The code :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define CAPACITY 50000
unsigned long hash(char *str) {
unsigned long int stringsum = 0;
for(; *str != '\0'; str++) {
stringsum += *str;
}
return stringsum % CAPACITY;
}
typedef struct item {
char *value;
char *key;
struct item *next;
struct item *prev;
} ht_item;
typedef struct hashtable {
ht_item **items;
int dim;
int count;
} HashTable;
HashTable* create_table(int size); HashTable* create_item(HashTable *table, char *value, char *key);
void print_table(HashTable* table, int dim);
int main(void) {
HashTable *table = create_table(CAPACITY);
table = create_item(table, "Giuseppe", "Nome");
print_table(table, CAPACITY);
return 0;
}
HashTable* create_item(HashTable *table, char *value, char *key) {
unsigned long index = hash(key);
printf("%u", index);
ht_item *_iterator; ht_item *prev;
for(_iterator = table->items[index], prev = NULL; _iterator != NULL; prev = _iterator, _iterator = _iterator->next);
_iterator = (ht_item*)malloc(sizeof(ht_item));
_iterator->key = (char*)malloc(200);
_iterator->value = (char*)malloc(200);
strcpy(_iterator->key, key);
strcpy(_iterator->value, value);
_iterator->next = NULL;
_iterator->prev = prev;
return table;
}
HashTable* create_table(int size)
{
HashTable *table = (HashTable*)malloc(sizeof(HashTable));
table->dim = size;
table->items = (ht_item**)calloc(size, sizeof(ht_item*));
for(int i = 0; i < size; i++){
table->items[i] = NULL;
}
return table;
}
void print_table(HashTable* table, int dim) {
for(int i = 0; i < CAPACITY; i++)
{
if(table->items[i] != NULL)
{ ht_item *_iterator = (ht_item*)malloc(sizeof(ht_item));
for(_iterator = table->items[i]; _iterator != NULL;
_iterator = _iterator->next)
{
printf("Key: %s\tValue: %s\n", _iterator->key, _iterator->value);
} free(_iterator);
}
}
}
Made some changes in your code. Please read through the blocks containing // CHANGE HERE comment.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define CAPACITY 50000
// CHANGE HERE - additional parameter, value to be used for modulo
unsigned long hash(char *str, unsigned int mod_value) {
unsigned long int stringsum = 0;
for(; *str != '\0'; str++) {
stringsum += *str;
}
// CHANGE HERE - use mod_value instead of CAPACITY
return stringsum % mod_value;
}
typedef struct item {
char *value;
char *key;
struct item *next;
struct item *prev;
} ht_item;
typedef struct hashtable {
ht_item **items;
int dim;
int count;
} HashTable;
HashTable* create_table(int size); HashTable* create_item(HashTable *table, char *value, char *key);
void print_table(HashTable* table, int dim);
int main(void) {
HashTable *table = create_table(CAPACITY);
table = create_item(table, "Giuseppe", "Nome");
print_table(table);
return 0;
}
HashTable* create_item(HashTable *table, char *value, char *key) {
// CHANGE HERE - function arguments validation
if (table == NULL)
{
return table;
}
if (value == NULL || key == NULL)
{
printf("Key or value is null\n");
return table;
}
// CHANGE HERE - pass table->dim to hash
unsigned long index = hash(key, table->dim);
printf("Index: %lu\n", index);
// CHANGE HERE - simplified the code a bit
ht_item* new_node = malloc(sizeof(ht_item));
new_node->key = malloc(200 * sizeof(char));
strncpy(new_node->key, key, 200);
new_node->value = malloc(200 * sizeof(char));
strncpy(new_node->value, value, 200);
// CHANGE HERE - if first node in index
if (table->items[index] == NULL)
{
table->items[index] = new_node;
return table;
}
ht_item *cur, *prev = NULL;
for(cur = table->items[index]; cur != NULL; prev = cur, cur = cur->next);
prev->next = new_node; // CHANGE HERE - it seems this line was missing
new_node->prev = prev;
new_node->next = NULL;
return table;
}
HashTable* create_table(int size)
{
HashTable *table = (HashTable*)malloc(sizeof(HashTable));
table->dim = size;
table->items = (ht_item**)calloc(size, sizeof(ht_item*));
for(int i = 0; i < size; i++){
table->items[i] = NULL;
}
return table;
}
void print_table(HashTable* table) {
// CHANGE HERE - function arguments validation
if (table == NULL)
{
printf("Table is null\n");
return;
}
// CHANGE HERE - change CAPACITY to dim
for(int i = 0; i < table->dim; i++)
{
//printf("i = %d [%d]\n", i, table->items[i] == NULL);
if(table->items[i] != NULL)
{
// CHANGE HERE - removed unnecessary malloc
ht_item *_iterator = NULL;
for(_iterator = table->items[i]; _iterator != NULL; _iterator = _iterator->next)
{
printf("Key: %s\tValue: %s\n", _iterator->key, _iterator->value);
}
}
}
}
The create_item function can and should be simplified.
I have put some comments inline.
HashTable* create_item(HashTable *table, char *value, char *key) {
// use modulo operator here, not in the hash function
unsigned long index = hash(key) % table->dim;
// nicer way of allocating
ht_item *insert = malloc(sizeof *insert);
// use strdup to avoid wasted memory and buffer overflows
insert->key = strdup(key);
insert->value = strdup(value);
// head insert rather than tail
insert->next = table->items[index];
table->items[index] = insert;
return table;
}
I dropped the use of the prev member. If you need that somewhere it's an exercise for you to add it. I don't think it's necessary for a simple hash table.

Pset5 Speller: I'm getting segmentation fault with small dictionary

When i used check50, my program was not giving any report as output. So i tried my code with small dictionary and i got a segmentation fault. I think it has do with my unload function
This is my code
// Implements a dictionary's functionality
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <strings.h>
#include <ctype.h>
#include "dictionary.h"
// 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 = 125;
int word_size = 0;
// Hash table
node *hashtable[N];
// Returns true if word is in dictionary else false
bool check(const char *word)
{
//Convert the word to lowercase
int length = strlen(word);
char copy[length + 1];
for (int i = 0; i < length; i++)
{
copy[i] = tolower(word[i]);
}
copy[length] = '\0';
//Find out the hash value
int hash_value = hash(copy);
node *tmp = hashtable[hash_value];
//Check if word is in dictionary
while(tmp != NULL)
{
if(strcasecmp(tmp->word, copy) == 0)
{
return true;
}
tmp = tmp->next;
}
return false;
}
// Hashes word to a number
unsigned int hash(const char *word)
{
//It's djb2 hash function
unsigned int hash = 5381;
int c;
while ((c = *word++))
hash = ((hash << 5) + hash) + c;
return hash % N;
}
// Loads dictionary into memory, returning true if successful else false
bool load(const char *dictionary)
{
//Initialize all indexs of hastable to NULL
for(int i =0; i < LENGTH + 1; i++)
{
hashtable[i] = NULL;
}
char word[LENGTH +1];
int pos;
FILE *dict = fopen(dictionary,"r");
if(dict == NULL)
{
return false;
}
//Scan every word...I think my problem is in here
while (fscanf(dict, "%s", word) != EOF)
{
word_size++;
int length = strlen(word);
char copy[length + 1];
for (int i = 0; i < length; i++)
{
copy[i] = tolower(word[i]);
}
copy[length] = '\0';
node *new_node = malloc(sizeof(node));
strcpy(new_node->word,copy);
new_node->next = NULL;
//Find the hash value
pos = hash(copy);
//Set the pointer of the new node to index of hashtable
new_node->next = hashtable[pos];
hashtable[pos] = new_node;
}
return true;
}
// Returns number of words in dictionary if loaded else 0 if not yet loaded
unsigned int size(void)
{
return word_size;
}
// Unloads dictionary from memory, returning true if successful else false
bool unload(void)
{
node *cursor = NULL;
node* tmp = NULL;
for(int i=0; i < N; i++)
{
while(cursor->next != NULL)
{
tmp = cursor;
cursor = cursor->next;
free(tmp);
}
free(cursor);
}
return true;
}
Can anyone say the write logic if my unload function is wrong. It works fine with large dictionary. I tried but couldn't figure out how to use debug50
In your unload () function you soon define
node *cursor = NULL;
but then, without assigning to cursor any other value, you dereference it:
while ( cursor->next != NULL )
Dereferencing a NULL pointer raises undefined behavior, and it will likely result in a segmentation fault.
The following action is free(cursor); that, with a NULL pointer, is not good as well.

How would I change the code to allow for words from a text file to populate the tree instead of hardcoding words?

I'm creating a Trie that reads from a "Dictonary.txt" and adds it to the tree. I would like to ask how to code a function that reads a text file and adds it to the tree. I haven't really found any tutorials that I could understand how to achieve this.
I hope you can help me. I would also be happy with some useful links that step by step teach me how it could possibly be done.
// C implementation of search and insert operations
// on Trie
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#define ARRAY_SIZE(a) sizeof(a)/sizeof(a[0])
// Alphabet size (# of symbols)
#define ALPHABET_SIZE (26)
// Converts key current character into index
// use only 'a' through 'z' and lower case
#define CHAR_TO_INDEX(c) ((int)c - (int)'a')
// trie node
struct TrieNode
{
struct TrieNode *children[ALPHABET_SIZE];
// isLeaf is true if the node represents
// end of a word
bool isLeaf;
};
// Returns new trie node (initialized to NULLs)
struct TrieNode *getNode(void)
{
struct TrieNode *pNode = NULL;
pNode = (struct TrieNode *)malloc(sizeof(struct TrieNode));
if (pNode)
{
int i;
pNode->isLeaf = false;
for (i = 0; i < ALPHABET_SIZE; i++)
pNode->children[i] = NULL;
}
return pNode;
}
// If not present, inserts key into trie
// If the key is prefix of trie node, just marks leaf node
void insert(struct TrieNode *root, const char *key)
{
int level;
int length = strlen(key);
int index;
struct TrieNode *pCrawl = root;
for (level = 0; level < length; level++)
{
index = CHAR_TO_INDEX(key[level]);
if (!pCrawl->children[index])
pCrawl->children[index] = getNode();
pCrawl = pCrawl->children[index];
}
// mark last node as leaf
pCrawl->isLeaf = true;
}
// Returns true if key presents in trie, else false
bool search(struct TrieNode *root, const char *key)
{
int level;
int length = strlen(key);
int index;
struct TrieNode *pCrawl = root;
for (level = 0; level < length; level++)
{
index = CHAR_TO_INDEX(key[level]);
if (!pCrawl->children[index])
return false;
pCrawl = pCrawl->children[index];
}
return (pCrawl != NULL && pCrawl->isLeaf);
}
// Driver
int main()
{
// Input keys (use only 'a' through 'z' and lower case)
char keys[][8] = {"the", "a", "there", "answer", "any",
"by", "bye", "their"};
char output[][32] = {"Not present in trie", "Present in trie"};
struct TrieNode *root = getNode();
// Construct trie
int i;
for (i = 0; i < ARRAY_SIZE(keys); i++)
insert(root, keys[i]);
// Search for different keys
printf("%s --- %s\n", "the", output[search(root, "the")] );
printf("%s --- %s\n", "these", output[search(root, "these")] );
printf("%s --- %s\n", "their", output[search(root, "their")] );
printf("%s --- %s\n", "thaw", output[search(root, "thaw")] );
return 0;
}

C Program: Create Linked List Using argv, argc, Segmentation Fault

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);
}
}

Adding and Finding node in LinkedList C

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)?

Resources