0

I'm trying to write a hash map with a dynamic array that stores a set of pointers to a linked list struct. I've been running into some strange memory issues. It seems like there are pointers in my array that shouldn't be.

I get an error where even though the index that should be generated from the single insert I'm doing is 16, as I loop through the array, there are some non-zero items:

start 
16 //(output of index from insert)
dumbtest //(get of nonexistent key)
0 // result of get of non-existent key
HashTable:
    0   0x0--
    0x0

    1   0x0--
    0x0

    2   0xeb-- //Why is this not 0x0?  
        0xeb

I end up with an error: Thread 1: EXC_BAD_ACCESS (code=1, address=0x80000002c)

from this line in the string library:

 _LIBCPP_INLINE_VISIBILITY
    bool __is_long() const _NOEXCEPT
        {return bool(__r_.first().__s.__size_ & __short_mask);}

I'm guessing that's the result of treating the junk in the array as a pointer and then trying to cast random bytes as the string of the "key" property.

Here is the class file

#include "hashtable.hpp"
using namespace std;

HashTable::HashTable()
{
    this->length = 32;
    this->buck = new LinkedList *[32];
};


unsigned int HashTable::hash(string key)
{
    int hash = 7;
    for (int i = 0; i < key.length(); i++) {
        hash = hash*31 * key[i];
    }
    hash = hash % this->length;
    return std::abs(hash);
}

int HashTable::find(string key)
{
    int index = this->hash(key);
    LinkedList *curr = this->buck[index];
    while(curr != nullptr)
    {
        if(curr->key == key)
        {
//            std::cout << curr;
            int val = curr->value;
            return val;
        }

        curr = curr->next;
    }
    return 0;
}


void HashTable::output()
{
    cout << "\nHashTable:\n";
    for(int i = 0; i < this->length; i++)
    {
        LinkedList *curr = this->buck[i];
        cout << "\t";
        cout << i;
        cout << "\t";
        cout << curr;
        cout << "--\n\t";
        while (curr != nullptr){
            cout << "\t";
            cout << curr;
            cout << ":";
            cout << curr->key;
            cout << ",";
            cout << curr->value;
            cout << ",";
            cout << curr->next;
            curr = curr->next;
        }
        cout << this->buck[i];
        cout << "\n";
        cout << "\n";
    }
}

void HashTable::insert(string key, int value)
{
    unsigned int index = this->hash(key);
    cout << index;
    LinkedList *curr = this->buck[index];
    LinkedList *ins = new LinkedList{
        key,
        value,
        NULL,
    };
    if (curr == nullptr){
        this->buck[index] = ins;
    }
    else
    {
        while(curr->next != nullptr)
        {
            curr = curr->next;
        }

        curr->next = ins;
    }
}



void HashTable::rehash()
{
    this->length = 2 * this->length;
    LinkedList** old = this->buck;
    this->buck = new LinkedList* [this->length];
    for (int i=0; i < this->length; i++)
    {
        LinkedList* curr = old[i];
        while(curr != nullptr)
        {
            this->insert(curr->key, curr->value);
            curr = curr->next;
        }
    }
}

And then my header

#ifndef hashtable_hpp
#define hashtable_hpp

#include <stdio.h>
#include <string>
#include <iostream>


struct LinkedList
{
    std::string key;
    int value;
    LinkedList *next;
};


class HashTable
{
private:
    int length;
    LinkedList** buck;
    unsigned int hash(std::string key);
    void rehash();

public:
    HashTable();
    int find(std::string key);
    void insert(std::string key, int value);
    bool remove(std::string key);
    void output();

};
#endif /* hashtable_hpp */

Here is the main file running it:

#include <stdio.h>
#include <string>
#include <iostream>
#include "hashtable/hashtable.hpp"

int main(int argc, const char * argv[])
{
    std::cout << "start\n";

    HashTable ht;
    ht.insert("test", 3);
//    ht.output();
    std::cout << "\ndumbtest\n";
    int dumbval = ht.find("testy");
    std::cout << dumbval;
    ht.output();

    std::cout << "\ntest\n";
    int val = ht.find("test");
    std::cout << val;

    std::cout << "\nend";
    return 0;
}

Here's the stack trace:

* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x80000002c)
  * frame #0: 0x00000001000021bd algStudy`std::__1::basic_ostream<char, std::__1::char_traits<char> >& std::__1::operator<<<char, std::__1::char_traits<char>, std::__1::allocator<char> >(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) [inlined] std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::__is_long(this="") const at string:1221
    frame #1: 0x00000001000021bd algStudy`std::__1::basic_ostream<char, std::__1::char_traits<char> >& std::__1::operator<<<char, std::__1::char_traits<char>, std::__1::allocator<char> >(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) [inlined] std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::__get_pointer(this="") const at string:1315
    frame #2: 0x00000001000021bd algStudy`std::__1::basic_ostream<char, std::__1::char_traits<char> >& std::__1::operator<<<char, std::__1::char_traits<char>, std::__1::allocator<char> >(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) [inlined] std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::data(this="") const at string:1129
    frame #3: 0x00000001000021bd algStudy`std::__1::basic_ostream<char, std::__1::char_traits<char> >& std::__1::operator<<<char, std::__1::char_traits<char>, std::__1::allocator<char> >(__os=0x00007fff9b480660, __str="") at ostream:1047
    frame #4: 0x0000000100002067 algStudy`HashTable::output(this=0x00007ffeefbff4c8) at hashtable.cpp:63
    frame #5: 0x00000001000014c5 algStudy`main(argc=1, argv=0x00007ffeefbff5e8) at main.cpp:23
    frame #6: 0x00007fff6388d015 libdyld.dylib`start + 1
    frame #7: 0x00007fff6388d015 libdyld.dylib`start + 1

Any ideas?

scl
  • 93
  • 9
  • 2
    You're not initializing `buck`, so its contents are whatever happens to be in that memory. If you want them to be initialized to `nullptr`, you'll need to do that explicitly. – Stephen Newell Apr 29 '18 at 21:02
  • So I have to loop through and set each value to nullptr in the constructor? – scl Apr 29 '18 at 21:06
  • nvm, got it, thanks `this->buck = new LinkedList *[32]()` – scl Apr 29 '18 at 21:10
  • 1
    You could use a vector instead of manual memory management – M.M Apr 29 '18 at 22:49
  • Yes, this is just for practice anyway, so I thought I would work on memory management while I'm at it. – scl May 01 '18 at 03:48

1 Answers1

1

From your code, I can tell you allocate the linked list from the heap, which means you cannot guarantee the Operating System helps you clear the allocated memory.

Realistically, nullptr means that it simply points to address NULL (zero) which you can not to read/write to it, so that pointer is "null".

There is a C language solution better than loop and setting to nullptr: Add this single line after you allocated HashTable::buck

memset(buck,0,sizeof(buck));

This line sets the bytes in the space of HashTable::buck to zero.

TravorLZH
  • 302
  • 1
  • 9