I am making my own dynamic allocator in C++. But I ran into a problem where I can't free my memory.
This is the code for Test.cpp:
#include "Memory/MemoryManager.h"
int main(){
initMemory(1);
int* p = allocate<int>();
int* q = allocate<int>();
int* r = allocate<int>();
cout<<p<<endl;
cout<<q<<endl;
cout<<r<<endl;
freeAddress<int>(q);
return 0;
}
MemoryManager.h:
#ifndef MEMORY_MANAGER_INCLUDED
#define MEMORY_MANAGER_INCLUDED
#include <iostream>
#include <map>
#include <memory>
using namespace std;
char* memory;
char* current;
map<void*, size_t> freePointers;
void initMemory(size_t size){
memory = (char*)malloc(size);
current = memory;
}
template<typename T> T* allocate(){
T* address = NULL;
for (auto p : freePointers){
if (p.second == sizeof(T)){
address = static_cast<T*>(p.first);
}
}
if (address == NULL){
address = new(current) T();
current += sizeof(T);
}
return address;
}
template<typename T> T* allocate(size_t size){
T* address = NULL;
for (auto p : freePointers){
if (p.second == sizeof(T) * size){
return static_cast<T*>(p.first);
}
}
if (address == NULL){
address = new(current) T[size];
current += sizeof(T) * size;
}
return address;
}
template<typename T> void freeAddress(T* address){
freePointers.insert({(void*)address, sizeof(*address)});
delete address;
}
template<typename T> void freeAddress(T* address, size_t size){
freePointers.insert({(void*)address, sizeof(*address) * size});
delete [] address;
}
#endif
Output:
0x55ee37729e70
0x55ee37729e74
0x55ee37729e78
0x55ee37729e70
0x55ee37729e78
free(): invalid pointer
I know that I can't delete pointers on stack memory but I am not using it at all. Also please point out if I am doing something wrong or performance expensize. Please help.
You allocate a large chunk of memory with malloc, then call delete on some pointer into the allocated memory.
In your code, q is (memory + sizeof(int)). This pointer has never been returned by an allocation and therefore cannot be freed. This is the reason for the error.
Moreover, mismatching malloc with delete is undefined behavior. You should instead call free() on the original memory pointer as cleanup in the end, and never call delete on individual values.
If you want to call the destructor in freeAddress, use address->~T();.
You are allocating a single 1-byte block of memory one time, in your initMemory(). You are using malloc() for that allocation, so that memory block must be freed with free() when you are done using it, but you are not doing that.
Your freeAddress() is delete'ing memory that was never allocated with new. Inside of allocate(), you are using placement-new instead of new, they are not the same thing. When you use placement-new, you have to call an object's destructor manually, not free() or delete it.
And you certainly do not want to free memory that you are going to reuse later. That defeats the whole purpose of caching "freed" memory.
And new[] uses more memory than you request, so that it can store info for delete[] to know know how many elements to free, and how to free them. You won't know how much extra overhead that is, as it is implementation defined. So it is not safe to use new[] in this situation.
Try something more like this instead
#include "Memory/MemoryManager.h"
int main(){
initMemory(sizeof(int) * 3);
int* p = allocate<int>();
int* q = allocate<int>();
int* r = allocate<int>();
cout << p << endl;
cout << q << endl;
cout << r << endl;
freeAddress<int>(q);
doneMemory();
return 0;
}
#ifndef MEMORY_MANAGER_INCLUDED
#define MEMORY_MANAGER_INCLUDED
#include <iostream>
#include <map>
#include <memory>
char* memory;
char* current;
size_t available;
std::map<void*, size_t> freePointers;
void initMemory(size_t size){
memory = (char*) malloc(size);
current = memory;
available = (memory) ? size : 0;
}
void doneMemory(){
freePointers.clear();
free(memory);
memory = current = nullptr;
available = 0;
}
template<typename T>
T* allocate(){
T *address = nullptr;
for (auto iter = freePointers.begin(); iter != freePointers.end(); ++iter){
if (iter->second == sizeof(T)){
address = static_cast<T*>(iter->first);
freePointers.erase(iter);
break;
}
}
if (!address){
if (available < sizeof(T)){
return nullptr;
}
address = static_cast<T*>(current);
current += sizeof(T);
available -= sizeof(T);
}
return new(address) T();
}
template<typename T>
T* allocate(size_t count){
T *address = nullptr;
size_t size = count * sizeof(T);
for (auto iter = freePointers.begin(); iter != freePointers.end(); ++iter){
if (iter->second == size){
address = static_cast<T*>(iter->first);
freePointers.erase(iter);
break;
}
}
if (!address){
if (available < size){
return nullptr;
}
address = static_cast<T*>(current);
current += size;
available -= size;
}
for(size_t i = 0; i < count; ++i)
new(address+i) T();
}
return address;
}
template<typename T>
void freeAddress(T* address){
address->~T();
freePointers.insert({(void*)address, sizeof(T)});
}
template<typename T>
void freeAddress(T* address, size_t count){
for (size_t i = 0; i < count; ++i)
address[i].~T();
freePointers.insert({(void*)address, sizeof(T) * count});
}
#endif
That being said, this isn't a very safe or robust memory allocator, but it should get you started.
If you really want to create a custom memory allocator, you should write a class that follows the Allocator strategy, and then you can use that allocator with standard C++ containers, like std::vector. Let the compiler and standard library handle most of the hard work for you.
void test(int *p2) {
*p2 = 3;}
int main()
{
int* p1, x = 5;
p1 = &x;
test(p1); // p1 and p2 are pointing to the same address which is x's address
printf("%d", x); //prints 3
this example 2 pointers pointing to the same address, which passing to the function by reference.
Now take this 2nd example
void test(int **p2) {
**p2 = 3;
}
int main()
{
int* p1, x = 5;
p1 = &x;
test(&p1); // p2 is pointing to p1 address
printf("%d", x);
so are double pointers necessary in these type of situations? especially with structured linked lists?
typedef struct NOde {
int data;
struct NOde* next;
}node;
void test(node *head) {
node* new_node = (node*)malloc(sizeof(node));
new_node->data = 5;
new_node->next = head;
head= new_node;
}
int main()
{
node* head=NULL;
test(head);
and why in this one, the head values in the main still NULL if it same concept as above?
Pointers (*p) are sufficient when you want to change the contents of the address the pointer is pointing at.
Double star pointers (**p) are necessary when you want to change the address the pointer is pointing at.
In the following code, inspect the outcome of the second printf statements especially.
#include <stdio.h>
#include <stdlib.h>
void swapValues(int *p, int val) {
*p = val;
}
void swapPointers(int **p, int *val) {
*p = val;
}
int main() {
int x, y;
int *p1 = &x;
int *p2 = &x;
x = 3;
y = 5;
printf("x = %d y = %d p1 = %d p2 = %d\n", x, y, *p1, *p2);
printf("p1 = %p p2 = %p\n", p1, p2);
swapValues(p1, y);
printf("x = %d y = %d p1 = %d p2 = %d\n", x, y, *p1, *p2);
printf("p1 = %p p2 = %p\n", p1, p2);
x = 3;
y = 5;
swapPointers(&p2, &y);
printf("x = %d y = %d p1 = %d p2 = %d\n", x, y, *p1, *p2);
printf("p1 = %p p2 = %p\n", p1, p2); // observe value of p2 here
return 0;
}
In C, all function calls are made by value. Which essentially means that the called function always gets its own copy of the arguments you pass to it. Same goes with the value you return from the function. There is always a copy of this value given back to the caller. The moment a function finishes execution, all arguments passed to it and local variables declared within it cease to exist.
For example:
int add(int a, int b)
{
int result = a + b;
return result;
}
int main()
{
int p = 3, q = 5;
int r = add(p,q);
}
In this case, a and b are copies of p and q respectively, and r is a copy of result. p, q and result no longer exist after add() has finished execution.
Now, this is fine for many common use-cases as in the example above. But what if you want to change the value of one of the variables in the calling function from within the called function? You then need to pass the address of the variable, so that the called function can indirectly access the variable in the calling function and update it.
Example:
void inc(int *p)
{
*p = *p + 1;
}
int main()
{
int a = 5;
inc(&a);
}
In this case, the called function gets a copy of the address of a, called p, using which it is able to update the memory location holding a indirectly. This is called dereferencing a pointer.
Now, to address your question, we need to take this one step further - what if we need to update a pointer in the calling function? We need to pass a pointer to the pointer - also called a double pointer.
In your example, we need to update head, which is already a pointer to a Node. So we need to pass the address of head, for which we need a double pointer.
Hence your code should be:
void test(node **phead)
{
node* new_node = (node*)malloc(sizeof(node));
new_node->data = 5;
new_node->next = *phead;
/* Note the dereferencing here - we update `head` indirectly through a pointer */
*phead = new_node;
}
test(&head);
Otherwise, we would be passing around a copy of head, which is a pointer, using which you can access the node that head points to, but not head itself. If you increment this pointer within your function, the change is not reflected outside, because this copy ceases to exist the moment the function returns.
PS: C++, unlike C, supports call by reference, which means the language transparently handles the pointer management and lets you directly update variables passed to you 'by reference'.
In your case no, because to assign the value you only need one pointer.
void test(int *p2) {
*p2 = 3;
}
Pointers to pointers are useful when you want to change the pointer.
A common use of pointers to pointers is methods that create something, but want to return something other than the pointer itself, e.g.
myerror_t create_foo(foo_t **p, int a, int b, int c)
{
if (a < 0 || b < c) return MYERR_INVALID_ARG;
*p = malloc(sizeof foo_t);
p->x = a * b * c;
return MYERR_SUCCESS;
}
Note that in C++, sometimes references are used when changing the value, and they can function in a very similar way.
void test(int &p2) {
p2 = 3;
}
Also note in C++, that throwing an exception, often from a constructor, is more common that a create_foo style method.
Foo::Foo(int a, int b, int c)
{
if (a < 0) throw std::invalid_argument("Foo a < 0");
if (b < c) throw std::invalid_argument("Foo b < c");
x = a * b * c;
}
If a factory function is desired, it might return the pointer and throw exceptions.
double pointers are needed if you are going to change the pointer itself in the function
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
int value1 = 10;
int value2 = 20;
void choose(int x, int **pointerToValue)
{
switch(x)
{
case 0:
*pointerToValue = &value1;
break;
case 1:
*pointerToValue = &value2;
break;
default:
*pointerToValue = NULL;
}
}
int main()
{
int *ptr;
int choice;
choose(choice = rand() & 1, &ptr);
//ptr value was changed in the function
printf("Choice = %d, value = %d\n", choice, ptr ? *ptr : 0);
}
Are double pointers necessary?
TL;DR:
The moment a function shall change the value of a pointer defined by the calling function, then yes, they are (and can even become triple, quatuple, ... pointers).
Longish answer:
To have a function change a variable of type T and this variable is defined by the caller, the caller needs to pass to the function a pointer to T, that is a T*.
The function then needs to dereference the T* variable using the * operator as shown in your 1st snippet:
void test(int *p2) {
*p2 = 3; /* p2 is dereferenced, so the assignment works on the variable it is pointing to. */
}
So if then T already is a pointer type then T* would be a pointer to a pointer.
This latter case should be the appearing within the last snippet you show. And it shall be handled exactly as in the 1st snippet. node * head shall be changed within test2(). So pass the address of head;
test2(&head);
To be able to do so the parameter of test2 need to be defined as a pointer to the type of head. head is node*, so a pointer to is is node**.
void test2(node ** phead)
To then change the value of the caller's head inside the function the pointer to head, namely phead needs be dereferenced:
*phead = ....
why in this one, the head values in the main still NULL if it same concept as above?"
Comparing the lines of your last snippet to the versions in my answer, you see that your code in fact is not using the "same concept" but misses a level of indirection, which your 1st snippet indeed uses ...;)
You would only pass a double (or triple, or whatever) pointer to a function if you need the function to write a new pointer value to the parameter, or if you're dealing with multiple-dimensioned data (an array of pointers to arrays (of pointers to arrays of ...)).
If you want a function to write to a parameter of type T, then you need to pass a pointer to T:
void foo( T *p )
{
*p = new_T_value(); // write a new value to the thing p points to
}
void bar( void )
{
T var;
foo( &var ); // foo writes a new value to var
}
Now let's replace T with the pointer type P *:
void foo( P **ptr )
{
*ptr = new_pointer_to_P_value;
}
void bar( void )
{
P *var;
foo( &var ); // write a new pointer value to var
}
Once more for emphasis, replace P with Q *:
void foo( Q ***ptr )
{
*ptr = new_pointer_to_pointer_to_Q_value;
}
void bar( void )
{
Q **var;
foo( &var ); // write a new value to var
}
The semantics are the same in all three cases, all that's changed is the level of indirection. For N levels of indirection in bar, you need N+1 levels of indirection in foo.
The other case for multiple indirection is multiple-dimensioned data (arrays of pointers to arrays of pointers to ...). For example:
void create_2d_arr( int ***arr, size_t rows, size_t cols )
{
*arr = malloc( rows * sizeof *(*arr) );
if ( *arr )
{
for ( size_t i = 0; i < rows; i++ )
{
(*arr)[i] = malloc( cols * sizeof *(*arr)[i] )
{
for ( size_t j = 0; j < cols; j++ )
{
(*arr)[i][j] = initial_value;
}
}
}
}
}
As to your example:
void test(node *head) {
node* new_node = (node*)malloc(sizeof(node));
new_node->data = 5;
new_node->next = head;
head= new_node;
Yes, in this case, if you want the change to head to be seen in main, you have to pass a pointer to the pointer:
void test(node **head) {
node* new_node = (node*)malloc(sizeof(node));
new_node->data = 5;
new_node->next = *head;
*head= new_node;
}
int main( void )
{
...
test( &head );
...
}
It's not necessary double pointer because p2 is in the scope of the function so in the main function is not visibile. And you don't want to change the pointer address but only the value of the pointed variable.
If ypu want to change a variable in a function you should pass it by reference in C or C++ meaning.
Consider your example of a singly-linked list. The variable head has a pointer type.
node* head=NULL;
So to change it in the function test you need to pass the variable by reference. For example
A C implementation passing by reference
void test( node **head, int data )
{
node *new_node = ( node* )malloc( sizeof( node ) );
new_node->data = data;
new_node->next = *head;
*head= new_node;
}
and a C++ implementation passing by reference
void test( node * &head, int data )
{
head = new node { data, head };
}
Without passing the head node by reference in the C function implementation the function deals with a copy of the value stored in head. You can imagine the function and its call the following way
test( head, 5 );
//...
void test( /*node *parm_head, int data*/ )
{
node *parm_head = head;
int data = 5;
node *new_node = ( node* )malloc( sizeof( node ) );
new_node->data = data;
new_node->next = *head;
*head= new_node;
}
That is function parameters are its local variables.
Consider another example when you need ay first to allocate an array to pointers to strings. For example
char **strings = malloc( 10 * sizeof( char * ) );
for ( size_t i = 0; i < 10; i++ )
{
strings[i] = malloc( 100 );
}
Now if you want to reallocate the original array by adding one more string then you gave to pass a pointer to this double pointer. For example
void add_one_more_string( char ***strings )
{
char **tmp = realloc( *strings, 11 ( sizeof( char * ) );
if ( tmp != NULL ) *stringvs = tmp;
//…
}
and call the function like
add_one_more_string( &strings );
I'm attempting to write a simple pool allocator for allocation and deallocation in c++ as part of a uni course task. We were given a reference to to git-user floooh's oryol engine, https://github.com/floooh/oryol/blob/master/code/Modules/Core/Memory/poolAllocator.h, hence I try to do something similar by splitting the pool into puddles that are allocated when needed. Starting with one and then incrementing as memory demands increase.
Each puddle in my case maintains its own free list of nodes and I fail already in creating the first puddle: I get segmentation fault when I try to access node struct data members. Below is my pool allocator class description along with constructor and function for adding a puddle. I commented in caps lock "SEGMENTATION FAULT" in allocNewPuddle() where it fails, line 10 in that function.
Class description:
template<class T> class memAllocator {
public:
memAllocator();
~memAllocator();
struct Puddle;
struct mNode {
mNode* nextN;
mNode* prevN;
uint puddle;
};
struct Puddle {
mNode* headN_free;
mNode* headN_occ;
};
uint numPuddles;
static const uint nodesInPuddle = 512;
static const uint maxPuddles = 512;
Puddle* puddles[maxPuddles];
uint nodeSize;
uint elemSize;
uint puddleStructSize;
void allocNewPuddle();
void* allocate();
void deallocate(void* obj);
void* findNextFreeNode();
template<typename... ARGS> T* create(ARGS&&... args);
void destroy(T* obj);
};
Constructor:
template<class T>
memAllocator<T>::memAllocator() // creates instance of allocator starting with one puddle allocated
{
this->numPuddles = 0;
this->nodeSize = sizeof(mNode);
this->elemSize = nodeSize + sizeof(T);
this->puddleStructSize = sizeof(Puddle);
allocNewPuddle();
}
Add a new puddle:
template<class T>
void memAllocator<T>::allocNewPuddle() // allocates a new puddle
{
// allocate memory for one puddle
assert(numPuddles < maxPuddles);
Puddle* newPuddle = (Puddle*) malloc(puddleStructSize + nodesInPuddle * elemSize);
// allocate nodes in free list pointed to by puddle struct
newPuddle->headN_free = (mNode*) (newPuddle + puddleStructSize + (nodesInPuddle-1)*elemSize);
for (int i = nodesInPuddle-2; i >= 0; i--) {
mNode* curNode = (mNode*) (newPuddle + puddleStructSize + i*elemSize);
// Fails here when attempting to access mNode struct members
curNode->puddle = numPuddles; // SEGMENTATION FAULT HERE ON FIRST ITERATION
curNode->prevN = nullptr;
curNode->nextN = newPuddle->headN_free;
curNode->nextN->prevN = curNode;
newPuddle->headN_free = curNode;
}
newPuddle->headN_occ = nullptr;
puddles[numPuddles] = newPuddle;
numPuddles++;
}
Here is my main.cc:
#include "memAllocator.h"
#include <iostream>
class Test {
public:
Test();
~Test();
int arr[5];
};
Test::Test() {
for (int i = 0; i < 5; i++) {
this->arr[i] = i;
}
}
Test::~Test() {
std::cout << "destructor called" << std::endl;
}
int main(int argc, char* argv[]) {
memAllocator<Test> memPool = memAllocator<Test> ();
Test* test = memPool.create();
for (int i = 0; i < 5; i++) {
std::cout << test->arr[i] << std::endl;
}
memPool.destroy(test);
for (int i = 0; i < 5; i++) {
std::cout << test->arr[i] << std::endl;
}
}
My guess is that I am doing something horribly naïve with c++ pointers, but from what I know the above should work. If not then I look forward to a good scolding.
Oh, and as you can see I'm not bothering to align memory since it is a small assignment, and as I understand this is not essential for it to work it only makes it faster, but is it possible this can cause wrong memory to be read and written to as more is demanded?
You have incorrect address calculation in line
mNode* curNode = (mNode*) (newPuddle + puddleStructSize + i*elemSize);
newPuddle is Puddle pointer, but you trying to add bytes. Thus you have new address far beyond end of allocated memory buffer. So you must add explicit cast to byte pointer (char, uint8_t etc)
mNode* curNode = (mNode*) ((char*)newPuddle + puddleStructSize + i*elemSize);
You must fix this line too
newPuddle->headN_free = (mNode*) (newPuddle + puddleStructSize + (nodesInPuddle-1)*elemSize);
I've been working on a memory pool allocator class, and no major problems have arisen, sans Visual Studio's debug assertion (_BLOCK_TYPE_IS_VALID(pHead->nBlockUse)) being thrown whenever I try to free the memory allocated by new with delete.
typedef uintptr_t uptr;
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef int8_t s8;
typedef int16_t s16;
typedef int32_t s32;
typedef int64_t s64;
struct FreeList
{
FreeList *next;
};
template<class T, u8 alignment, u32 poolSize>
class PoolAllocator
{
private:
u8 _paddedSize; // The size in bytes of each allocated chunk of memory.
u32 _numAllocations;// The number of allocations made by the pool.
u32 _freeBytes; // The number of bytes left in the pool.
u32 _usedBytes; // The number of bytes currently occupied in the pool.
FreeList* _freeListHead; // A pointer to a freed space in memory.
public:
PoolAllocator() :
_paddedSize((sizeof(T) > sizeof(uptr) ? sizeof(T) : sizeof(uptr))),
_numAllocations(0),
_freeBytes(0),
_usedBytes(0),
_freeListHead(nullptr)
{
_freeListHead = reinterpret_cast<FreeList*>(operator new (_paddedSize * poolSize));
_freeBytes = _paddedSize * poolSize;
uptr current = reinterpret_cast<uptr>(_freeListHead);
uptr last = current + (_paddedSize * poolSize);
for (int i = 0; i < poolSize-1; i++)
{
uptr next = current + _paddedSize;
(reinterpret_cast<FreeList*>(current))->next = reinterpret_cast<FreeList*>(next);
current += _paddedSize;
}
reinterpret_cast<FreeList*>(current)->next = nullptr;
}
T *allocate()
{
if (_freeListHead != nullptr && _freeBytes >= _paddedSize) // Make sure the pool has memory left
{
uptr *toReturn = reinterpret_cast<uptr*>(_freeListHead); // Cast the pointer to a modifiable data type.
_freeListHead = _freeListHead->next; // VITAL THAT THIS IS BEFORE SETTING DATA TO 0.
*toReturn = 0; // Set the data at the memory location to 0.
_freeBytes -= _paddedSize;
_usedBytes += _paddedSize;
_numAllocations++;
printf("Allocated %d bytes of memory at %p.\n", _paddedSize, toReturn);
return reinterpret_cast<T*>(toReturn);
}
else
{
printf("Pool allocator out of memory! Returning nullptr.\n");
return nullptr;
}
}
void free(T **ptr)
{
FreeList *newHead = reinterpret_cast<FreeList*>(*ptr);
*ptr = nullptr;
newHead->next = _freeListHead;
_freeListHead = newHead;
_freeBytes += _paddedSize;
_usedBytes -= _paddedSize;
_numAllocations--;
printf("Freed %d bytes of memory at %p.\n", _paddedSize, _freeListHead);
}
void clear()
{
assert(_usedBytes == 0);
FreeList *head = _freeListHead;
while (head != 0)
{
FreeList *next = head->next;
delete reinterpret_cast<T*>(head);
head = next;
}
_paddedSize = 0;
_numAllocations = 0;
_freeBytes = 0;
_usedBytes = 0;
_freeListHead = nullptr;
}
};
The test code I am using:
int main()
{
PoolAllocator<int, 4, 4> pool;
int *a, *b, *c, *d, *e;
a = pool.allocate();
b = pool.allocate();
c = pool.allocate();
d = pool.allocate();
pool.free(&a);
e = pool.allocate();
printf("A | %p\t%d\nB | %p\t%d\nC | %p\t%d\nD | %p\t%d\nE | %p\t%d\n", a, 0, b, *b, c, *c, d, *d, e, *e);
pool.free(&b);
pool.free(&c);
pool.free(&d);
pool.free(&e);
pool.clear();
return 0;
}
The problem area lies here:
void clear()
{
assert(_usedBytes == 0);
FreeList *head = _freeListHead;
while (head != 0)
{
FreeList *next = head->next;
delete reinterpret_cast<T*>(head); // Debug assert
head = next;
}
_paddedSize = 0;
_numAllocations = 0;
_freeBytes = 0;
_usedBytes = 0;
_freeListHead = nullptr;
}
What this code is supposed to do is increment through the linked list of memory locations where individual pieces of data of type T are being stored. My reasoning for this being a valid piece of code is that, because the initially allocated memory is divided into pieces the size of sizeof(T). Therefor, I assumed that it would be appropriate to typecast the memory address to a pointer of type T* so that the individual block of memory would be completely deallocated. This would be done for the entire linked list, ensuring that all allocated memory is freed. However, when I run the code, it always throws the assert on the delete.
Stepping through the code showed that after the first iteration of typecasting and deleteing, the pointers take on strange values (the next variable, for instance:
next 0x004b2864 {next=0xfeeefeee {next=??? } } FreeList *
Whereas before, it was
next 0x006c2864 {next=0x006c2860 {next=0x006c285c {next=0x00000000
} } } FreeList *
just as it should be). I have tried this approach many different times in many different ways, including typecasting to void* instead of T*.
This problem has had me stumped for days, and any help would be appreciated. Thank you.
reinterpret_cast<T*>(head)->~T();
I tested this and not getting the assert anymore by replacing delete reinterpret_cast(head);
You can't call delete on head because the memory wasn't allocated by calling new T();
This will not compile:
struct FreeList
{
FreeList *next;
};
... but the following will:
struct FreeList
{
struct FreeList *next;
};
Otherwise, an error about incomplete struct definition or unknown type FreeList will be raised by the compiler.
I'm trying to implement hash table, but I'm getting a runtime error in the for loop of createHashTable() function. Can anyone please tell me why is it showing this "runtime error"? Is it StackOverflow error?
#include <iostream>
using namespace std;
#define LOAD_FACTOR 20
struct ListNode{
int data;
struct ListNode *next;
};
struct HashTableNode{
int bCount; // number of elements in the block
struct ListNode *next;
};
struct HashTable{
int tSize; // table size
int count; // total number of elements in the table
struct HashTableNode **hashTableNodeArray;
};
int hashFunction(struct HashTable *h, int data){
return data % h->tSize;
}
struct HashTable * createHashTable(int numberOfElements){
struct HashTable *h = new HashTable;
h->count = 0;
h->tSize = numberOfElements / LOAD_FACTOR;
h->hashTableNodeArray = new HashTableNode *[h->tSize];
for(int i = 0; i < h->tSize; ++i){
// this is where it is showing runtime error
h->hashTableNodeArray[i]->bCount = 0;
h->hashTableNodeArray[i]->next = nullptr;
}
return h;
}
void deleteHashTable(struct HashTable *h){
struct ListNode *node, *tmp;
for(int i = 0; i < h->tSize; ++i){
node = h->hashTableNodeArray[i]->next;
while(node != nullptr){
tmp = node;
node = node->next;
delete tmp;
}
}
delete[] h->hashTableNodeArray;
delete h;
}
int main(int argc, char **argv){
struct HashTable *h = createHashTable(220);
deleteHashTable(h);
return 0;
}
h->hashTableNodeArray = new HashTableNode *[h->tSize];
This allocates an array of pointers, but not the actual hashtablenodes. In the following loop you try to write to them which is undefined behaviour.
You are missing in your loop:
h->hashTableNodeArray[i] = new HashTableNode;
The problem is here:
h->hashTableNodeArray = new HashTableNode *[h->tSize];
for(int i = 0; i < h->tSize; ++i){
// this is where it is showing runtime error
h->hashTableNodeArray[i]->bCount = 0;
h->hashTableNodeArray[i]->next = nullptr;
}
You allocate an array of pointers, but don't actually make the pointers point anywhere valid which means their values are indeterminate (and in reality seemingly random). You then proceed to dereference these uninitialized pointers, and write to memory using the pointers, without knowing where in memory you will write.
This leads to undefined behavior, and most likely your crash.
The solution? Either don't use pointers, or explicitly allocate the memory for the pointers. My recommendation is to stop using pointers altogether, create proper copy- and move-constructors, and use std::vector instead.