C hashtable implementation
up vote
3
down vote
favorite
The following code is an implementation of an hashtable in C.
hashTable.c:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "hashTable.h"
int hashCode(int key) {
return key % SIZE;
}
/*
By given key returns a pointer to a DataItem with the same key
Input: Pointer to hashArray array, int key value
Output: If key found - the pointer to a DataItem else NULL
*/
DataItem *getValueByKey (DataItem** hashArray, int key) {
/* Get the hash */
int hashIndex = hashCode(key);
/* Move in array until an empty */
while(hashArray[hashIndex] != NULL) {
if(hashArray[hashIndex]->key == key)
return hashArray[hashIndex];
/* Go to next cell */
++hashIndex;
/* Wrap around the table */
hashIndex %= SIZE;
}
return NULL;
}
/*
Adding a DataItem to a hashArray
Input: Pointer to hashArray array, int key value, char* (string) data value
Output: None
*/
void putValueForKey (DataItem** hashArray, int key, char *data) {
/* Get the hash */
int hashIndex = hashCode(key);
DataItem *item = (DataItem*) malloc(sizeof(DataItem));
item->data = data;
item->key = key;
/* Move in array until an empty or deleted cell */
while(hashArray[hashIndex] != NULL) {
/* Go to next cell */
++hashIndex;
/* Wrap around the table */
hashIndex %= SIZE;
}
hashArray[hashIndex] = item;
}
/*
Deleting a DataItem node from an hash array
Input: Pointer to hashArray array, pointer to the item we want to delete
Output: The deleted item pointer
*/
DataItem* deleteHash (DataItem** hashArray, DataItem* item) {
int key;
int hashIndex;
if (item == NULL)
{
return NULL;
}
key = item->key;
/* Get the hash */
hashIndex = hashCode(key);
/* Move in array until an empty */
while(hashArray[hashIndex] != NULL) {
if(hashArray[hashIndex]->key == key) {
DataItem* temp = hashArray[hashIndex];
/* Assign a dummy item at deleted position */
hashArray[hashIndex] = NULL;
return temp;
}
/* Go to next cell */
++hashIndex;
/* Wrap around the table */
hashIndex %= SIZE;
}
return NULL;
}
/*
displaying an hash array
Input: Pointer to hashArray array
Output: None
*/
void displayHashTable (DataItem** hashArray) {
int i = 0;
for (i = 0; i < SIZE; i++) {
if (hashArray[i] != NULL)
printf(" (%d,%s)",hashArray[i]->key,hashArray[i]->data);
else
printf(" ~~ ");
}
printf("n");
}
/*
Freeing an hash array
Input: Pointer to hashArray array
Output: None
*/
void freeHashTable (DataItem** hashArray) {
int i = 0;
for (i = 0; i < SIZE; ++i)
{
if (hashArray[i] != NULL) {
free(hashArray[i]);
}
}
}
/*
Initialize an hash array by setting all the nodes to NULL
Input: Pointer to hashArray array
Output: None
*/
void initializeHashArray (DataItem** hashArray) {
int i = 0;
for (i = 0; i < SIZE; i++) {
hashArray[i] = NULL;
}
}
int main() {
DataItem* hashArray[SIZE];
initializeHashArray(hashArray);
putValueForKey(hashArray, 100, "MAIN");
putValueForKey(hashArray, 107, "LOOP");
putValueForKey(hashArray, 121, "END");
putValueForKey(hashArray, 122, "STR");
putValueForKey(hashArray, 129, "LENGTHHHHHHHHHHHHHHHHHHHHH");
putValueForKey(hashArray, 132, "K");
putValueForKey(hashArray, 133, "M1");
displayHashTable(hashArray);
DataItem* item = getValueByKey(hashArray, 100);
if (item != NULL) {
printf("Element found: %sn", item->data);
}
else {
printf("Element not foundn");
}
deleteHash(hashArray, item);
item = getValueByKey(hashArray, 100);
if (item != NULL) {
printf("Element found: %sn", item->data);
}
else {
printf("Element not foundn");
}
displayHashTable(hashArray);
freeHashTable(hashArray);
}
I believe this code is well commented and very understandable, if you think it's not please let me know.
The main is there just for testing and will not be part of the final code.
My main concerns are:
The free function (freeHashTable) because I don't know how to test it so I don't know if it's working.
The while loops that might get into an infinite loop condition for some reason.
Also this is the first time I'm trying to implement hash table, so if I'm missing something from the logical aspect of hash code explanation will be very welcomed.
hashTable.h:
#define SIZE 20
struct DataItem {
char *data;
int key;
}typedef DataItem;
DataItem *getValueByKey (DataItem** hashArray, int key);
void putValueForKey (DataItem** hashArray, int key, char *data);
DataItem* deleteHash (DataItem** hashArray, DataItem* item);
void displayHashTable (DataItem** hashArray);
void freeHashTable (DataItem** hashArray);
void initializeHashArray (DataItem** hashArray);
Thanks in advance!
c hash-table
add a comment |
up vote
3
down vote
favorite
The following code is an implementation of an hashtable in C.
hashTable.c:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "hashTable.h"
int hashCode(int key) {
return key % SIZE;
}
/*
By given key returns a pointer to a DataItem with the same key
Input: Pointer to hashArray array, int key value
Output: If key found - the pointer to a DataItem else NULL
*/
DataItem *getValueByKey (DataItem** hashArray, int key) {
/* Get the hash */
int hashIndex = hashCode(key);
/* Move in array until an empty */
while(hashArray[hashIndex] != NULL) {
if(hashArray[hashIndex]->key == key)
return hashArray[hashIndex];
/* Go to next cell */
++hashIndex;
/* Wrap around the table */
hashIndex %= SIZE;
}
return NULL;
}
/*
Adding a DataItem to a hashArray
Input: Pointer to hashArray array, int key value, char* (string) data value
Output: None
*/
void putValueForKey (DataItem** hashArray, int key, char *data) {
/* Get the hash */
int hashIndex = hashCode(key);
DataItem *item = (DataItem*) malloc(sizeof(DataItem));
item->data = data;
item->key = key;
/* Move in array until an empty or deleted cell */
while(hashArray[hashIndex] != NULL) {
/* Go to next cell */
++hashIndex;
/* Wrap around the table */
hashIndex %= SIZE;
}
hashArray[hashIndex] = item;
}
/*
Deleting a DataItem node from an hash array
Input: Pointer to hashArray array, pointer to the item we want to delete
Output: The deleted item pointer
*/
DataItem* deleteHash (DataItem** hashArray, DataItem* item) {
int key;
int hashIndex;
if (item == NULL)
{
return NULL;
}
key = item->key;
/* Get the hash */
hashIndex = hashCode(key);
/* Move in array until an empty */
while(hashArray[hashIndex] != NULL) {
if(hashArray[hashIndex]->key == key) {
DataItem* temp = hashArray[hashIndex];
/* Assign a dummy item at deleted position */
hashArray[hashIndex] = NULL;
return temp;
}
/* Go to next cell */
++hashIndex;
/* Wrap around the table */
hashIndex %= SIZE;
}
return NULL;
}
/*
displaying an hash array
Input: Pointer to hashArray array
Output: None
*/
void displayHashTable (DataItem** hashArray) {
int i = 0;
for (i = 0; i < SIZE; i++) {
if (hashArray[i] != NULL)
printf(" (%d,%s)",hashArray[i]->key,hashArray[i]->data);
else
printf(" ~~ ");
}
printf("n");
}
/*
Freeing an hash array
Input: Pointer to hashArray array
Output: None
*/
void freeHashTable (DataItem** hashArray) {
int i = 0;
for (i = 0; i < SIZE; ++i)
{
if (hashArray[i] != NULL) {
free(hashArray[i]);
}
}
}
/*
Initialize an hash array by setting all the nodes to NULL
Input: Pointer to hashArray array
Output: None
*/
void initializeHashArray (DataItem** hashArray) {
int i = 0;
for (i = 0; i < SIZE; i++) {
hashArray[i] = NULL;
}
}
int main() {
DataItem* hashArray[SIZE];
initializeHashArray(hashArray);
putValueForKey(hashArray, 100, "MAIN");
putValueForKey(hashArray, 107, "LOOP");
putValueForKey(hashArray, 121, "END");
putValueForKey(hashArray, 122, "STR");
putValueForKey(hashArray, 129, "LENGTHHHHHHHHHHHHHHHHHHHHH");
putValueForKey(hashArray, 132, "K");
putValueForKey(hashArray, 133, "M1");
displayHashTable(hashArray);
DataItem* item = getValueByKey(hashArray, 100);
if (item != NULL) {
printf("Element found: %sn", item->data);
}
else {
printf("Element not foundn");
}
deleteHash(hashArray, item);
item = getValueByKey(hashArray, 100);
if (item != NULL) {
printf("Element found: %sn", item->data);
}
else {
printf("Element not foundn");
}
displayHashTable(hashArray);
freeHashTable(hashArray);
}
I believe this code is well commented and very understandable, if you think it's not please let me know.
The main is there just for testing and will not be part of the final code.
My main concerns are:
The free function (freeHashTable) because I don't know how to test it so I don't know if it's working.
The while loops that might get into an infinite loop condition for some reason.
Also this is the first time I'm trying to implement hash table, so if I'm missing something from the logical aspect of hash code explanation will be very welcomed.
hashTable.h:
#define SIZE 20
struct DataItem {
char *data;
int key;
}typedef DataItem;
DataItem *getValueByKey (DataItem** hashArray, int key);
void putValueForKey (DataItem** hashArray, int key, char *data);
DataItem* deleteHash (DataItem** hashArray, DataItem* item);
void displayHashTable (DataItem** hashArray);
void freeHashTable (DataItem** hashArray);
void initializeHashArray (DataItem** hashArray);
Thanks in advance!
c hash-table
add a comment |
up vote
3
down vote
favorite
up vote
3
down vote
favorite
The following code is an implementation of an hashtable in C.
hashTable.c:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "hashTable.h"
int hashCode(int key) {
return key % SIZE;
}
/*
By given key returns a pointer to a DataItem with the same key
Input: Pointer to hashArray array, int key value
Output: If key found - the pointer to a DataItem else NULL
*/
DataItem *getValueByKey (DataItem** hashArray, int key) {
/* Get the hash */
int hashIndex = hashCode(key);
/* Move in array until an empty */
while(hashArray[hashIndex] != NULL) {
if(hashArray[hashIndex]->key == key)
return hashArray[hashIndex];
/* Go to next cell */
++hashIndex;
/* Wrap around the table */
hashIndex %= SIZE;
}
return NULL;
}
/*
Adding a DataItem to a hashArray
Input: Pointer to hashArray array, int key value, char* (string) data value
Output: None
*/
void putValueForKey (DataItem** hashArray, int key, char *data) {
/* Get the hash */
int hashIndex = hashCode(key);
DataItem *item = (DataItem*) malloc(sizeof(DataItem));
item->data = data;
item->key = key;
/* Move in array until an empty or deleted cell */
while(hashArray[hashIndex] != NULL) {
/* Go to next cell */
++hashIndex;
/* Wrap around the table */
hashIndex %= SIZE;
}
hashArray[hashIndex] = item;
}
/*
Deleting a DataItem node from an hash array
Input: Pointer to hashArray array, pointer to the item we want to delete
Output: The deleted item pointer
*/
DataItem* deleteHash (DataItem** hashArray, DataItem* item) {
int key;
int hashIndex;
if (item == NULL)
{
return NULL;
}
key = item->key;
/* Get the hash */
hashIndex = hashCode(key);
/* Move in array until an empty */
while(hashArray[hashIndex] != NULL) {
if(hashArray[hashIndex]->key == key) {
DataItem* temp = hashArray[hashIndex];
/* Assign a dummy item at deleted position */
hashArray[hashIndex] = NULL;
return temp;
}
/* Go to next cell */
++hashIndex;
/* Wrap around the table */
hashIndex %= SIZE;
}
return NULL;
}
/*
displaying an hash array
Input: Pointer to hashArray array
Output: None
*/
void displayHashTable (DataItem** hashArray) {
int i = 0;
for (i = 0; i < SIZE; i++) {
if (hashArray[i] != NULL)
printf(" (%d,%s)",hashArray[i]->key,hashArray[i]->data);
else
printf(" ~~ ");
}
printf("n");
}
/*
Freeing an hash array
Input: Pointer to hashArray array
Output: None
*/
void freeHashTable (DataItem** hashArray) {
int i = 0;
for (i = 0; i < SIZE; ++i)
{
if (hashArray[i] != NULL) {
free(hashArray[i]);
}
}
}
/*
Initialize an hash array by setting all the nodes to NULL
Input: Pointer to hashArray array
Output: None
*/
void initializeHashArray (DataItem** hashArray) {
int i = 0;
for (i = 0; i < SIZE; i++) {
hashArray[i] = NULL;
}
}
int main() {
DataItem* hashArray[SIZE];
initializeHashArray(hashArray);
putValueForKey(hashArray, 100, "MAIN");
putValueForKey(hashArray, 107, "LOOP");
putValueForKey(hashArray, 121, "END");
putValueForKey(hashArray, 122, "STR");
putValueForKey(hashArray, 129, "LENGTHHHHHHHHHHHHHHHHHHHHH");
putValueForKey(hashArray, 132, "K");
putValueForKey(hashArray, 133, "M1");
displayHashTable(hashArray);
DataItem* item = getValueByKey(hashArray, 100);
if (item != NULL) {
printf("Element found: %sn", item->data);
}
else {
printf("Element not foundn");
}
deleteHash(hashArray, item);
item = getValueByKey(hashArray, 100);
if (item != NULL) {
printf("Element found: %sn", item->data);
}
else {
printf("Element not foundn");
}
displayHashTable(hashArray);
freeHashTable(hashArray);
}
I believe this code is well commented and very understandable, if you think it's not please let me know.
The main is there just for testing and will not be part of the final code.
My main concerns are:
The free function (freeHashTable) because I don't know how to test it so I don't know if it's working.
The while loops that might get into an infinite loop condition for some reason.
Also this is the first time I'm trying to implement hash table, so if I'm missing something from the logical aspect of hash code explanation will be very welcomed.
hashTable.h:
#define SIZE 20
struct DataItem {
char *data;
int key;
}typedef DataItem;
DataItem *getValueByKey (DataItem** hashArray, int key);
void putValueForKey (DataItem** hashArray, int key, char *data);
DataItem* deleteHash (DataItem** hashArray, DataItem* item);
void displayHashTable (DataItem** hashArray);
void freeHashTable (DataItem** hashArray);
void initializeHashArray (DataItem** hashArray);
Thanks in advance!
c hash-table
The following code is an implementation of an hashtable in C.
hashTable.c:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "hashTable.h"
int hashCode(int key) {
return key % SIZE;
}
/*
By given key returns a pointer to a DataItem with the same key
Input: Pointer to hashArray array, int key value
Output: If key found - the pointer to a DataItem else NULL
*/
DataItem *getValueByKey (DataItem** hashArray, int key) {
/* Get the hash */
int hashIndex = hashCode(key);
/* Move in array until an empty */
while(hashArray[hashIndex] != NULL) {
if(hashArray[hashIndex]->key == key)
return hashArray[hashIndex];
/* Go to next cell */
++hashIndex;
/* Wrap around the table */
hashIndex %= SIZE;
}
return NULL;
}
/*
Adding a DataItem to a hashArray
Input: Pointer to hashArray array, int key value, char* (string) data value
Output: None
*/
void putValueForKey (DataItem** hashArray, int key, char *data) {
/* Get the hash */
int hashIndex = hashCode(key);
DataItem *item = (DataItem*) malloc(sizeof(DataItem));
item->data = data;
item->key = key;
/* Move in array until an empty or deleted cell */
while(hashArray[hashIndex] != NULL) {
/* Go to next cell */
++hashIndex;
/* Wrap around the table */
hashIndex %= SIZE;
}
hashArray[hashIndex] = item;
}
/*
Deleting a DataItem node from an hash array
Input: Pointer to hashArray array, pointer to the item we want to delete
Output: The deleted item pointer
*/
DataItem* deleteHash (DataItem** hashArray, DataItem* item) {
int key;
int hashIndex;
if (item == NULL)
{
return NULL;
}
key = item->key;
/* Get the hash */
hashIndex = hashCode(key);
/* Move in array until an empty */
while(hashArray[hashIndex] != NULL) {
if(hashArray[hashIndex]->key == key) {
DataItem* temp = hashArray[hashIndex];
/* Assign a dummy item at deleted position */
hashArray[hashIndex] = NULL;
return temp;
}
/* Go to next cell */
++hashIndex;
/* Wrap around the table */
hashIndex %= SIZE;
}
return NULL;
}
/*
displaying an hash array
Input: Pointer to hashArray array
Output: None
*/
void displayHashTable (DataItem** hashArray) {
int i = 0;
for (i = 0; i < SIZE; i++) {
if (hashArray[i] != NULL)
printf(" (%d,%s)",hashArray[i]->key,hashArray[i]->data);
else
printf(" ~~ ");
}
printf("n");
}
/*
Freeing an hash array
Input: Pointer to hashArray array
Output: None
*/
void freeHashTable (DataItem** hashArray) {
int i = 0;
for (i = 0; i < SIZE; ++i)
{
if (hashArray[i] != NULL) {
free(hashArray[i]);
}
}
}
/*
Initialize an hash array by setting all the nodes to NULL
Input: Pointer to hashArray array
Output: None
*/
void initializeHashArray (DataItem** hashArray) {
int i = 0;
for (i = 0; i < SIZE; i++) {
hashArray[i] = NULL;
}
}
int main() {
DataItem* hashArray[SIZE];
initializeHashArray(hashArray);
putValueForKey(hashArray, 100, "MAIN");
putValueForKey(hashArray, 107, "LOOP");
putValueForKey(hashArray, 121, "END");
putValueForKey(hashArray, 122, "STR");
putValueForKey(hashArray, 129, "LENGTHHHHHHHHHHHHHHHHHHHHH");
putValueForKey(hashArray, 132, "K");
putValueForKey(hashArray, 133, "M1");
displayHashTable(hashArray);
DataItem* item = getValueByKey(hashArray, 100);
if (item != NULL) {
printf("Element found: %sn", item->data);
}
else {
printf("Element not foundn");
}
deleteHash(hashArray, item);
item = getValueByKey(hashArray, 100);
if (item != NULL) {
printf("Element found: %sn", item->data);
}
else {
printf("Element not foundn");
}
displayHashTable(hashArray);
freeHashTable(hashArray);
}
I believe this code is well commented and very understandable, if you think it's not please let me know.
The main is there just for testing and will not be part of the final code.
My main concerns are:
The free function (freeHashTable) because I don't know how to test it so I don't know if it's working.
The while loops that might get into an infinite loop condition for some reason.
Also this is the first time I'm trying to implement hash table, so if I'm missing something from the logical aspect of hash code explanation will be very welcomed.
hashTable.h:
#define SIZE 20
struct DataItem {
char *data;
int key;
}typedef DataItem;
DataItem *getValueByKey (DataItem** hashArray, int key);
void putValueForKey (DataItem** hashArray, int key, char *data);
DataItem* deleteHash (DataItem** hashArray, DataItem* item);
void displayHashTable (DataItem** hashArray);
void freeHashTable (DataItem** hashArray);
void initializeHashArray (DataItem** hashArray);
Thanks in advance!
c hash-table
c hash-table
asked Aug 1 '17 at 18:39
Yonlif
1546
1546
add a comment |
add a comment |
4 Answers
4
active
oldest
votes
up vote
0
down vote
accepted
Fix the compiler warnings
These should all be straightforward to address:
gcc -std=c17 -fPIC -g -Wall -Wextra -Wwrite-strings -Wno-parentheses -Wpedantic -Warray-bounds -Wconversion 171784.c -o 171784
171784.c:11:1: warning: ‘typedef’ is not at beginning of declaration [-Wold-style-declaration]
}typedef DataItem;
^
171784.c: In function ‘main’:
171784.c:163:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 100, "MAIN");
^~~~~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
171784.c:164:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 107, "LOOP");
^~~~~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
171784.c:165:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 121, "END");
^~~~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
171784.c:166:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 122, "STR");
^~~~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
171784.c:167:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 129, "LENGTHHHHHHHHHHHHHHHHHHHHH");
^~~~~~~~~~~~~~~~~~~~~~~~~~~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
171784.c:168:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 132, "K");
^~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
171784.c:169:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 133, "M1");
^~~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
Fix the memory leak
valgrind -q --leak-check=full ./171784
(100,MAIN) (121,END) (122,STR) ~~ ~~ ~~ ~~ (107,LOOP) ~~ (129,LENGTHHHHHHHHHHHHHHHHHHHHH) ~~ ~~ (132,K) (133,M1) ~~ ~~ ~~ ~~ ~~ ~~
Element found: MAIN
Element not found
~~ (121,END) (122,STR) ~~ ~~ ~~ ~~ (107,LOOP) ~~ (129,LENGTHHHHHHHHHHHHHHHHHHHHH) ~~ ~~ (132,K) (133,M1) ~~ ~~ ~~ ~~ ~~ ~~
==26310== 16 bytes in 1 blocks are definitely lost in loss record 1 of 1
==26310== at 0x483577F: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==26310== by 0x109271: putValueForKey (171784.c:60)
==26310== by 0x10955D: main (171784.c:163)
==26310==
It seems that the first item to be inserted was never freed.
That seems to be a bug in the test program: deleteHash()
transfers ownership to its caller, meaning that main()
is now responsible for freeing that item. It's easily fixed:
free(deleteHash(hashArray, item));
However, this is a warning that you might not have thought sufficiently about ownership of objects, in general. Give this some more thought, and try to make your code easy to use correctly, and harder to use incorrectly.
Style points
I noticed a few things whilst looking at this, but haven't done a thorough inspection. Other reviews have addressed quite a few style points; I'll try to make only new observations here.
Don't cast the result of
malloc()
and family, and prefer to use the size of the variable rather than its type, to ensure consistency (and make that consistency obvious):
DataItem *item = malloc(sizeof *item);
It's fine to pass a null pointer to
free()
, so there's very little point pre-testing the argument:
for (size_t i = 0; i < SIZE; ++i) {
free(hashArray[i]);
}
Add more tests
The main()
function performs only a very basic test. We should add tests that
- attempt to insert duplicate keys and entries with negative keys
- attempt to insert more than
SIZE
entries - attempt to remove keys that aren't present
And we might consider tests of misuse, such as
- calling
freeHashTable()
twice
Advanced testing would also substitute malloc()
with a library that allows failure injection, so that we can test the error paths.
add a comment |
up vote
3
down vote
while
loops can never terminate if the table is full. If all the slots're used and you're trying to insert a new element or look for a key that isn't there, it'll keep wrapping around as there're noNULL
elements in the array.You can fix it by terminating the loop after
SIZE
iterations, but it's only part of the problem.The real question is: what is the desired behavior in this case? Being unable to keep more than
SIZE
elements (which should be known at compile time) is a serious limitation. In fact, a hash table that can hold only 20 elements is likely to be quite useless in practice (if the amount of data is so small, a simple array is unlikely to cause any performance issues). Ideally, the table should grow when there's too little room left. Roughly speaking, you should allocate a larger array, rehash the elements, but them into it, and delete old array.Even if you decide to keep it fixed-size for simplicity, you need to indicate that the table is full somehow. It shouldn't silently fail to insert a new element. Using error codes is a typical way to do it in C.
You can run your program under valgrind (or some other memory-checking tool) to see if it leaks memory. If it does, your
free...
function's wrong (or is called improperly).Assuming that
malloc
always returns a valid pointer is dangerous. It may not. You need to check if its return value is notNULL
before working with it.deleteHash
is not a good name. It doesn't delete a hash. It deletes a key. It should be called correspondingly.It's not clear how you code behaves if the user tries to insert a key that's already in the table. Does it allow duplicates? What value does it return upon lookup in this case? Whatever the policy is, document it.
I will fix the while loop thanks! This code is just a part from a bigger project im working on and I'm allowed to decided on a max size of the hash table, but thanks any way. I didn't know these kind of programs exist, I will use them. I totally forgot about the malloc. So changing it to deleteKeyFromHashTable is good (it's pretty long) Since it's part of a bigger project I'm the user so there will be no double input. Thanks alot once again!
– Yonlif
Aug 1 '17 at 20:03
Just another small question, what should I do if I can't malloc any more?
– Yonlif
Aug 1 '17 at 20:11
@Yonlif You should fail with some error (you can return an error code from this function, check for it in themain
function, write a message to the screen and exit). That's not something your program can normally recover from.
– kraskevich
Aug 1 '17 at 20:20
add a comment |
up vote
2
down vote
Bug
If you insert a key, it might get pushed forward in the array because other keys are in the way. Then if you delete one of those keys that were in the way, you can never retrieve the first key. For example, imagine that key A
and key B
hash to the same index. Then if you do this :
putValueForKey(hash, A, data1); // Ends up in slot [0]
putValueForKey(hash, B, data1); // Ends up in slot [1]
deleteHash(hash, A); // Now slot [0] is NULL
Then you can never find key B
again:
// Here, ret becomes NULL because the hash table can't find B any more!
ret = getValueByKey(hash, B);
In order to fix this bug, your deletion function needs to be a lot more complicated. You should read this wikipedia article on how to delete an item when using linear probing. Better yet, you could choose a different collision strategy than linear probing. Here are some alternatives.
add a comment |
up vote
1
down vote
The while loops that might get into an infinite loop condition for some reason.
Bug: ability to generate negative hash indexes leads to UB, which includes the above concern.
putValueForKey(hashArray, -100, "minus");
Should key
have a negative value, so will hashCode()
return a negative value which causes out-of-bounds array access.
Various ways to handle that. For me I would use an unsigned type like unsigned
or size_t
. Also amend calling code.
// int hashCode(int key) {
unsigned hashCode(int key) {
return (unsigned) key % SIZE;
}
Other idea:
Consider prime values for HASH
Certainly hashCode()
here is a simple function and not truly meant to hash the key
highly uniformly across [0...SIZE)
. Yet remember that if the hash calculation before the final % SIZE
is good, finding the remainder with any SIZE
will be good too.
On the other hand (OTOH), if the hash calculation before the % SIZE
is weak, using a prime for SIZE
will likely to make it better. Example
// #define SIZE 20
#define SIZE 23
unsigned hashCode(int key) {
return (unsigned) key % SIZE;
}
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f171784%2fc-hashtable-implementation%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
0
down vote
accepted
Fix the compiler warnings
These should all be straightforward to address:
gcc -std=c17 -fPIC -g -Wall -Wextra -Wwrite-strings -Wno-parentheses -Wpedantic -Warray-bounds -Wconversion 171784.c -o 171784
171784.c:11:1: warning: ‘typedef’ is not at beginning of declaration [-Wold-style-declaration]
}typedef DataItem;
^
171784.c: In function ‘main’:
171784.c:163:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 100, "MAIN");
^~~~~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
171784.c:164:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 107, "LOOP");
^~~~~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
171784.c:165:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 121, "END");
^~~~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
171784.c:166:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 122, "STR");
^~~~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
171784.c:167:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 129, "LENGTHHHHHHHHHHHHHHHHHHHHH");
^~~~~~~~~~~~~~~~~~~~~~~~~~~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
171784.c:168:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 132, "K");
^~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
171784.c:169:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 133, "M1");
^~~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
Fix the memory leak
valgrind -q --leak-check=full ./171784
(100,MAIN) (121,END) (122,STR) ~~ ~~ ~~ ~~ (107,LOOP) ~~ (129,LENGTHHHHHHHHHHHHHHHHHHHHH) ~~ ~~ (132,K) (133,M1) ~~ ~~ ~~ ~~ ~~ ~~
Element found: MAIN
Element not found
~~ (121,END) (122,STR) ~~ ~~ ~~ ~~ (107,LOOP) ~~ (129,LENGTHHHHHHHHHHHHHHHHHHHHH) ~~ ~~ (132,K) (133,M1) ~~ ~~ ~~ ~~ ~~ ~~
==26310== 16 bytes in 1 blocks are definitely lost in loss record 1 of 1
==26310== at 0x483577F: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==26310== by 0x109271: putValueForKey (171784.c:60)
==26310== by 0x10955D: main (171784.c:163)
==26310==
It seems that the first item to be inserted was never freed.
That seems to be a bug in the test program: deleteHash()
transfers ownership to its caller, meaning that main()
is now responsible for freeing that item. It's easily fixed:
free(deleteHash(hashArray, item));
However, this is a warning that you might not have thought sufficiently about ownership of objects, in general. Give this some more thought, and try to make your code easy to use correctly, and harder to use incorrectly.
Style points
I noticed a few things whilst looking at this, but haven't done a thorough inspection. Other reviews have addressed quite a few style points; I'll try to make only new observations here.
Don't cast the result of
malloc()
and family, and prefer to use the size of the variable rather than its type, to ensure consistency (and make that consistency obvious):
DataItem *item = malloc(sizeof *item);
It's fine to pass a null pointer to
free()
, so there's very little point pre-testing the argument:
for (size_t i = 0; i < SIZE; ++i) {
free(hashArray[i]);
}
Add more tests
The main()
function performs only a very basic test. We should add tests that
- attempt to insert duplicate keys and entries with negative keys
- attempt to insert more than
SIZE
entries - attempt to remove keys that aren't present
And we might consider tests of misuse, such as
- calling
freeHashTable()
twice
Advanced testing would also substitute malloc()
with a library that allows failure injection, so that we can test the error paths.
add a comment |
up vote
0
down vote
accepted
Fix the compiler warnings
These should all be straightforward to address:
gcc -std=c17 -fPIC -g -Wall -Wextra -Wwrite-strings -Wno-parentheses -Wpedantic -Warray-bounds -Wconversion 171784.c -o 171784
171784.c:11:1: warning: ‘typedef’ is not at beginning of declaration [-Wold-style-declaration]
}typedef DataItem;
^
171784.c: In function ‘main’:
171784.c:163:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 100, "MAIN");
^~~~~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
171784.c:164:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 107, "LOOP");
^~~~~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
171784.c:165:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 121, "END");
^~~~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
171784.c:166:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 122, "STR");
^~~~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
171784.c:167:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 129, "LENGTHHHHHHHHHHHHHHHHHHHHH");
^~~~~~~~~~~~~~~~~~~~~~~~~~~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
171784.c:168:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 132, "K");
^~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
171784.c:169:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 133, "M1");
^~~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
Fix the memory leak
valgrind -q --leak-check=full ./171784
(100,MAIN) (121,END) (122,STR) ~~ ~~ ~~ ~~ (107,LOOP) ~~ (129,LENGTHHHHHHHHHHHHHHHHHHHHH) ~~ ~~ (132,K) (133,M1) ~~ ~~ ~~ ~~ ~~ ~~
Element found: MAIN
Element not found
~~ (121,END) (122,STR) ~~ ~~ ~~ ~~ (107,LOOP) ~~ (129,LENGTHHHHHHHHHHHHHHHHHHHHH) ~~ ~~ (132,K) (133,M1) ~~ ~~ ~~ ~~ ~~ ~~
==26310== 16 bytes in 1 blocks are definitely lost in loss record 1 of 1
==26310== at 0x483577F: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==26310== by 0x109271: putValueForKey (171784.c:60)
==26310== by 0x10955D: main (171784.c:163)
==26310==
It seems that the first item to be inserted was never freed.
That seems to be a bug in the test program: deleteHash()
transfers ownership to its caller, meaning that main()
is now responsible for freeing that item. It's easily fixed:
free(deleteHash(hashArray, item));
However, this is a warning that you might not have thought sufficiently about ownership of objects, in general. Give this some more thought, and try to make your code easy to use correctly, and harder to use incorrectly.
Style points
I noticed a few things whilst looking at this, but haven't done a thorough inspection. Other reviews have addressed quite a few style points; I'll try to make only new observations here.
Don't cast the result of
malloc()
and family, and prefer to use the size of the variable rather than its type, to ensure consistency (and make that consistency obvious):
DataItem *item = malloc(sizeof *item);
It's fine to pass a null pointer to
free()
, so there's very little point pre-testing the argument:
for (size_t i = 0; i < SIZE; ++i) {
free(hashArray[i]);
}
Add more tests
The main()
function performs only a very basic test. We should add tests that
- attempt to insert duplicate keys and entries with negative keys
- attempt to insert more than
SIZE
entries - attempt to remove keys that aren't present
And we might consider tests of misuse, such as
- calling
freeHashTable()
twice
Advanced testing would also substitute malloc()
with a library that allows failure injection, so that we can test the error paths.
add a comment |
up vote
0
down vote
accepted
up vote
0
down vote
accepted
Fix the compiler warnings
These should all be straightforward to address:
gcc -std=c17 -fPIC -g -Wall -Wextra -Wwrite-strings -Wno-parentheses -Wpedantic -Warray-bounds -Wconversion 171784.c -o 171784
171784.c:11:1: warning: ‘typedef’ is not at beginning of declaration [-Wold-style-declaration]
}typedef DataItem;
^
171784.c: In function ‘main’:
171784.c:163:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 100, "MAIN");
^~~~~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
171784.c:164:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 107, "LOOP");
^~~~~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
171784.c:165:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 121, "END");
^~~~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
171784.c:166:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 122, "STR");
^~~~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
171784.c:167:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 129, "LENGTHHHHHHHHHHHHHHHHHHHHH");
^~~~~~~~~~~~~~~~~~~~~~~~~~~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
171784.c:168:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 132, "K");
^~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
171784.c:169:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 133, "M1");
^~~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
Fix the memory leak
valgrind -q --leak-check=full ./171784
(100,MAIN) (121,END) (122,STR) ~~ ~~ ~~ ~~ (107,LOOP) ~~ (129,LENGTHHHHHHHHHHHHHHHHHHHHH) ~~ ~~ (132,K) (133,M1) ~~ ~~ ~~ ~~ ~~ ~~
Element found: MAIN
Element not found
~~ (121,END) (122,STR) ~~ ~~ ~~ ~~ (107,LOOP) ~~ (129,LENGTHHHHHHHHHHHHHHHHHHHHH) ~~ ~~ (132,K) (133,M1) ~~ ~~ ~~ ~~ ~~ ~~
==26310== 16 bytes in 1 blocks are definitely lost in loss record 1 of 1
==26310== at 0x483577F: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==26310== by 0x109271: putValueForKey (171784.c:60)
==26310== by 0x10955D: main (171784.c:163)
==26310==
It seems that the first item to be inserted was never freed.
That seems to be a bug in the test program: deleteHash()
transfers ownership to its caller, meaning that main()
is now responsible for freeing that item. It's easily fixed:
free(deleteHash(hashArray, item));
However, this is a warning that you might not have thought sufficiently about ownership of objects, in general. Give this some more thought, and try to make your code easy to use correctly, and harder to use incorrectly.
Style points
I noticed a few things whilst looking at this, but haven't done a thorough inspection. Other reviews have addressed quite a few style points; I'll try to make only new observations here.
Don't cast the result of
malloc()
and family, and prefer to use the size of the variable rather than its type, to ensure consistency (and make that consistency obvious):
DataItem *item = malloc(sizeof *item);
It's fine to pass a null pointer to
free()
, so there's very little point pre-testing the argument:
for (size_t i = 0; i < SIZE; ++i) {
free(hashArray[i]);
}
Add more tests
The main()
function performs only a very basic test. We should add tests that
- attempt to insert duplicate keys and entries with negative keys
- attempt to insert more than
SIZE
entries - attempt to remove keys that aren't present
And we might consider tests of misuse, such as
- calling
freeHashTable()
twice
Advanced testing would also substitute malloc()
with a library that allows failure injection, so that we can test the error paths.
Fix the compiler warnings
These should all be straightforward to address:
gcc -std=c17 -fPIC -g -Wall -Wextra -Wwrite-strings -Wno-parentheses -Wpedantic -Warray-bounds -Wconversion 171784.c -o 171784
171784.c:11:1: warning: ‘typedef’ is not at beginning of declaration [-Wold-style-declaration]
}typedef DataItem;
^
171784.c: In function ‘main’:
171784.c:163:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 100, "MAIN");
^~~~~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
171784.c:164:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 107, "LOOP");
^~~~~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
171784.c:165:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 121, "END");
^~~~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
171784.c:166:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 122, "STR");
^~~~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
171784.c:167:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 129, "LENGTHHHHHHHHHHHHHHHHHHHHH");
^~~~~~~~~~~~~~~~~~~~~~~~~~~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
171784.c:168:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 132, "K");
^~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
171784.c:169:36: warning: passing argument 3 of ‘putValueForKey’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
putValueForKey(hashArray, 133, "M1");
^~~~
171784.c:56:58: note: expected ‘char *’ but argument is of type ‘const char *’
void putValueForKey(DataItem** hashArray, int key, char *data) {
~~~~~~^~~~
Fix the memory leak
valgrind -q --leak-check=full ./171784
(100,MAIN) (121,END) (122,STR) ~~ ~~ ~~ ~~ (107,LOOP) ~~ (129,LENGTHHHHHHHHHHHHHHHHHHHHH) ~~ ~~ (132,K) (133,M1) ~~ ~~ ~~ ~~ ~~ ~~
Element found: MAIN
Element not found
~~ (121,END) (122,STR) ~~ ~~ ~~ ~~ (107,LOOP) ~~ (129,LENGTHHHHHHHHHHHHHHHHHHHHH) ~~ ~~ (132,K) (133,M1) ~~ ~~ ~~ ~~ ~~ ~~
==26310== 16 bytes in 1 blocks are definitely lost in loss record 1 of 1
==26310== at 0x483577F: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==26310== by 0x109271: putValueForKey (171784.c:60)
==26310== by 0x10955D: main (171784.c:163)
==26310==
It seems that the first item to be inserted was never freed.
That seems to be a bug in the test program: deleteHash()
transfers ownership to its caller, meaning that main()
is now responsible for freeing that item. It's easily fixed:
free(deleteHash(hashArray, item));
However, this is a warning that you might not have thought sufficiently about ownership of objects, in general. Give this some more thought, and try to make your code easy to use correctly, and harder to use incorrectly.
Style points
I noticed a few things whilst looking at this, but haven't done a thorough inspection. Other reviews have addressed quite a few style points; I'll try to make only new observations here.
Don't cast the result of
malloc()
and family, and prefer to use the size of the variable rather than its type, to ensure consistency (and make that consistency obvious):
DataItem *item = malloc(sizeof *item);
It's fine to pass a null pointer to
free()
, so there's very little point pre-testing the argument:
for (size_t i = 0; i < SIZE; ++i) {
free(hashArray[i]);
}
Add more tests
The main()
function performs only a very basic test. We should add tests that
- attempt to insert duplicate keys and entries with negative keys
- attempt to insert more than
SIZE
entries - attempt to remove keys that aren't present
And we might consider tests of misuse, such as
- calling
freeHashTable()
twice
Advanced testing would also substitute malloc()
with a library that allows failure injection, so that we can test the error paths.
answered Dec 6 at 10:43
Toby Speight
23.2k638110
23.2k638110
add a comment |
add a comment |
up vote
3
down vote
while
loops can never terminate if the table is full. If all the slots're used and you're trying to insert a new element or look for a key that isn't there, it'll keep wrapping around as there're noNULL
elements in the array.You can fix it by terminating the loop after
SIZE
iterations, but it's only part of the problem.The real question is: what is the desired behavior in this case? Being unable to keep more than
SIZE
elements (which should be known at compile time) is a serious limitation. In fact, a hash table that can hold only 20 elements is likely to be quite useless in practice (if the amount of data is so small, a simple array is unlikely to cause any performance issues). Ideally, the table should grow when there's too little room left. Roughly speaking, you should allocate a larger array, rehash the elements, but them into it, and delete old array.Even if you decide to keep it fixed-size for simplicity, you need to indicate that the table is full somehow. It shouldn't silently fail to insert a new element. Using error codes is a typical way to do it in C.
You can run your program under valgrind (or some other memory-checking tool) to see if it leaks memory. If it does, your
free...
function's wrong (or is called improperly).Assuming that
malloc
always returns a valid pointer is dangerous. It may not. You need to check if its return value is notNULL
before working with it.deleteHash
is not a good name. It doesn't delete a hash. It deletes a key. It should be called correspondingly.It's not clear how you code behaves if the user tries to insert a key that's already in the table. Does it allow duplicates? What value does it return upon lookup in this case? Whatever the policy is, document it.
I will fix the while loop thanks! This code is just a part from a bigger project im working on and I'm allowed to decided on a max size of the hash table, but thanks any way. I didn't know these kind of programs exist, I will use them. I totally forgot about the malloc. So changing it to deleteKeyFromHashTable is good (it's pretty long) Since it's part of a bigger project I'm the user so there will be no double input. Thanks alot once again!
– Yonlif
Aug 1 '17 at 20:03
Just another small question, what should I do if I can't malloc any more?
– Yonlif
Aug 1 '17 at 20:11
@Yonlif You should fail with some error (you can return an error code from this function, check for it in themain
function, write a message to the screen and exit). That's not something your program can normally recover from.
– kraskevich
Aug 1 '17 at 20:20
add a comment |
up vote
3
down vote
while
loops can never terminate if the table is full. If all the slots're used and you're trying to insert a new element or look for a key that isn't there, it'll keep wrapping around as there're noNULL
elements in the array.You can fix it by terminating the loop after
SIZE
iterations, but it's only part of the problem.The real question is: what is the desired behavior in this case? Being unable to keep more than
SIZE
elements (which should be known at compile time) is a serious limitation. In fact, a hash table that can hold only 20 elements is likely to be quite useless in practice (if the amount of data is so small, a simple array is unlikely to cause any performance issues). Ideally, the table should grow when there's too little room left. Roughly speaking, you should allocate a larger array, rehash the elements, but them into it, and delete old array.Even if you decide to keep it fixed-size for simplicity, you need to indicate that the table is full somehow. It shouldn't silently fail to insert a new element. Using error codes is a typical way to do it in C.
You can run your program under valgrind (or some other memory-checking tool) to see if it leaks memory. If it does, your
free...
function's wrong (or is called improperly).Assuming that
malloc
always returns a valid pointer is dangerous. It may not. You need to check if its return value is notNULL
before working with it.deleteHash
is not a good name. It doesn't delete a hash. It deletes a key. It should be called correspondingly.It's not clear how you code behaves if the user tries to insert a key that's already in the table. Does it allow duplicates? What value does it return upon lookup in this case? Whatever the policy is, document it.
I will fix the while loop thanks! This code is just a part from a bigger project im working on and I'm allowed to decided on a max size of the hash table, but thanks any way. I didn't know these kind of programs exist, I will use them. I totally forgot about the malloc. So changing it to deleteKeyFromHashTable is good (it's pretty long) Since it's part of a bigger project I'm the user so there will be no double input. Thanks alot once again!
– Yonlif
Aug 1 '17 at 20:03
Just another small question, what should I do if I can't malloc any more?
– Yonlif
Aug 1 '17 at 20:11
@Yonlif You should fail with some error (you can return an error code from this function, check for it in themain
function, write a message to the screen and exit). That's not something your program can normally recover from.
– kraskevich
Aug 1 '17 at 20:20
add a comment |
up vote
3
down vote
up vote
3
down vote
while
loops can never terminate if the table is full. If all the slots're used and you're trying to insert a new element or look for a key that isn't there, it'll keep wrapping around as there're noNULL
elements in the array.You can fix it by terminating the loop after
SIZE
iterations, but it's only part of the problem.The real question is: what is the desired behavior in this case? Being unable to keep more than
SIZE
elements (which should be known at compile time) is a serious limitation. In fact, a hash table that can hold only 20 elements is likely to be quite useless in practice (if the amount of data is so small, a simple array is unlikely to cause any performance issues). Ideally, the table should grow when there's too little room left. Roughly speaking, you should allocate a larger array, rehash the elements, but them into it, and delete old array.Even if you decide to keep it fixed-size for simplicity, you need to indicate that the table is full somehow. It shouldn't silently fail to insert a new element. Using error codes is a typical way to do it in C.
You can run your program under valgrind (or some other memory-checking tool) to see if it leaks memory. If it does, your
free...
function's wrong (or is called improperly).Assuming that
malloc
always returns a valid pointer is dangerous. It may not. You need to check if its return value is notNULL
before working with it.deleteHash
is not a good name. It doesn't delete a hash. It deletes a key. It should be called correspondingly.It's not clear how you code behaves if the user tries to insert a key that's already in the table. Does it allow duplicates? What value does it return upon lookup in this case? Whatever the policy is, document it.
while
loops can never terminate if the table is full. If all the slots're used and you're trying to insert a new element or look for a key that isn't there, it'll keep wrapping around as there're noNULL
elements in the array.You can fix it by terminating the loop after
SIZE
iterations, but it's only part of the problem.The real question is: what is the desired behavior in this case? Being unable to keep more than
SIZE
elements (which should be known at compile time) is a serious limitation. In fact, a hash table that can hold only 20 elements is likely to be quite useless in practice (if the amount of data is so small, a simple array is unlikely to cause any performance issues). Ideally, the table should grow when there's too little room left. Roughly speaking, you should allocate a larger array, rehash the elements, but them into it, and delete old array.Even if you decide to keep it fixed-size for simplicity, you need to indicate that the table is full somehow. It shouldn't silently fail to insert a new element. Using error codes is a typical way to do it in C.
You can run your program under valgrind (or some other memory-checking tool) to see if it leaks memory. If it does, your
free...
function's wrong (or is called improperly).Assuming that
malloc
always returns a valid pointer is dangerous. It may not. You need to check if its return value is notNULL
before working with it.deleteHash
is not a good name. It doesn't delete a hash. It deletes a key. It should be called correspondingly.It's not clear how you code behaves if the user tries to insert a key that's already in the table. Does it allow duplicates? What value does it return upon lookup in this case? Whatever the policy is, document it.
answered Aug 1 '17 at 19:52
kraskevich
5,325918
5,325918
I will fix the while loop thanks! This code is just a part from a bigger project im working on and I'm allowed to decided on a max size of the hash table, but thanks any way. I didn't know these kind of programs exist, I will use them. I totally forgot about the malloc. So changing it to deleteKeyFromHashTable is good (it's pretty long) Since it's part of a bigger project I'm the user so there will be no double input. Thanks alot once again!
– Yonlif
Aug 1 '17 at 20:03
Just another small question, what should I do if I can't malloc any more?
– Yonlif
Aug 1 '17 at 20:11
@Yonlif You should fail with some error (you can return an error code from this function, check for it in themain
function, write a message to the screen and exit). That's not something your program can normally recover from.
– kraskevich
Aug 1 '17 at 20:20
add a comment |
I will fix the while loop thanks! This code is just a part from a bigger project im working on and I'm allowed to decided on a max size of the hash table, but thanks any way. I didn't know these kind of programs exist, I will use them. I totally forgot about the malloc. So changing it to deleteKeyFromHashTable is good (it's pretty long) Since it's part of a bigger project I'm the user so there will be no double input. Thanks alot once again!
– Yonlif
Aug 1 '17 at 20:03
Just another small question, what should I do if I can't malloc any more?
– Yonlif
Aug 1 '17 at 20:11
@Yonlif You should fail with some error (you can return an error code from this function, check for it in themain
function, write a message to the screen and exit). That's not something your program can normally recover from.
– kraskevich
Aug 1 '17 at 20:20
I will fix the while loop thanks! This code is just a part from a bigger project im working on and I'm allowed to decided on a max size of the hash table, but thanks any way. I didn't know these kind of programs exist, I will use them. I totally forgot about the malloc. So changing it to deleteKeyFromHashTable is good (it's pretty long) Since it's part of a bigger project I'm the user so there will be no double input. Thanks alot once again!
– Yonlif
Aug 1 '17 at 20:03
I will fix the while loop thanks! This code is just a part from a bigger project im working on and I'm allowed to decided on a max size of the hash table, but thanks any way. I didn't know these kind of programs exist, I will use them. I totally forgot about the malloc. So changing it to deleteKeyFromHashTable is good (it's pretty long) Since it's part of a bigger project I'm the user so there will be no double input. Thanks alot once again!
– Yonlif
Aug 1 '17 at 20:03
Just another small question, what should I do if I can't malloc any more?
– Yonlif
Aug 1 '17 at 20:11
Just another small question, what should I do if I can't malloc any more?
– Yonlif
Aug 1 '17 at 20:11
@Yonlif You should fail with some error (you can return an error code from this function, check for it in the
main
function, write a message to the screen and exit). That's not something your program can normally recover from.– kraskevich
Aug 1 '17 at 20:20
@Yonlif You should fail with some error (you can return an error code from this function, check for it in the
main
function, write a message to the screen and exit). That's not something your program can normally recover from.– kraskevich
Aug 1 '17 at 20:20
add a comment |
up vote
2
down vote
Bug
If you insert a key, it might get pushed forward in the array because other keys are in the way. Then if you delete one of those keys that were in the way, you can never retrieve the first key. For example, imagine that key A
and key B
hash to the same index. Then if you do this :
putValueForKey(hash, A, data1); // Ends up in slot [0]
putValueForKey(hash, B, data1); // Ends up in slot [1]
deleteHash(hash, A); // Now slot [0] is NULL
Then you can never find key B
again:
// Here, ret becomes NULL because the hash table can't find B any more!
ret = getValueByKey(hash, B);
In order to fix this bug, your deletion function needs to be a lot more complicated. You should read this wikipedia article on how to delete an item when using linear probing. Better yet, you could choose a different collision strategy than linear probing. Here are some alternatives.
add a comment |
up vote
2
down vote
Bug
If you insert a key, it might get pushed forward in the array because other keys are in the way. Then if you delete one of those keys that were in the way, you can never retrieve the first key. For example, imagine that key A
and key B
hash to the same index. Then if you do this :
putValueForKey(hash, A, data1); // Ends up in slot [0]
putValueForKey(hash, B, data1); // Ends up in slot [1]
deleteHash(hash, A); // Now slot [0] is NULL
Then you can never find key B
again:
// Here, ret becomes NULL because the hash table can't find B any more!
ret = getValueByKey(hash, B);
In order to fix this bug, your deletion function needs to be a lot more complicated. You should read this wikipedia article on how to delete an item when using linear probing. Better yet, you could choose a different collision strategy than linear probing. Here are some alternatives.
add a comment |
up vote
2
down vote
up vote
2
down vote
Bug
If you insert a key, it might get pushed forward in the array because other keys are in the way. Then if you delete one of those keys that were in the way, you can never retrieve the first key. For example, imagine that key A
and key B
hash to the same index. Then if you do this :
putValueForKey(hash, A, data1); // Ends up in slot [0]
putValueForKey(hash, B, data1); // Ends up in slot [1]
deleteHash(hash, A); // Now slot [0] is NULL
Then you can never find key B
again:
// Here, ret becomes NULL because the hash table can't find B any more!
ret = getValueByKey(hash, B);
In order to fix this bug, your deletion function needs to be a lot more complicated. You should read this wikipedia article on how to delete an item when using linear probing. Better yet, you could choose a different collision strategy than linear probing. Here are some alternatives.
Bug
If you insert a key, it might get pushed forward in the array because other keys are in the way. Then if you delete one of those keys that were in the way, you can never retrieve the first key. For example, imagine that key A
and key B
hash to the same index. Then if you do this :
putValueForKey(hash, A, data1); // Ends up in slot [0]
putValueForKey(hash, B, data1); // Ends up in slot [1]
deleteHash(hash, A); // Now slot [0] is NULL
Then you can never find key B
again:
// Here, ret becomes NULL because the hash table can't find B any more!
ret = getValueByKey(hash, B);
In order to fix this bug, your deletion function needs to be a lot more complicated. You should read this wikipedia article on how to delete an item when using linear probing. Better yet, you could choose a different collision strategy than linear probing. Here are some alternatives.
answered Aug 1 '17 at 23:20
JS1
27.2k32976
27.2k32976
add a comment |
add a comment |
up vote
1
down vote
The while loops that might get into an infinite loop condition for some reason.
Bug: ability to generate negative hash indexes leads to UB, which includes the above concern.
putValueForKey(hashArray, -100, "minus");
Should key
have a negative value, so will hashCode()
return a negative value which causes out-of-bounds array access.
Various ways to handle that. For me I would use an unsigned type like unsigned
or size_t
. Also amend calling code.
// int hashCode(int key) {
unsigned hashCode(int key) {
return (unsigned) key % SIZE;
}
Other idea:
Consider prime values for HASH
Certainly hashCode()
here is a simple function and not truly meant to hash the key
highly uniformly across [0...SIZE)
. Yet remember that if the hash calculation before the final % SIZE
is good, finding the remainder with any SIZE
will be good too.
On the other hand (OTOH), if the hash calculation before the % SIZE
is weak, using a prime for SIZE
will likely to make it better. Example
// #define SIZE 20
#define SIZE 23
unsigned hashCode(int key) {
return (unsigned) key % SIZE;
}
add a comment |
up vote
1
down vote
The while loops that might get into an infinite loop condition for some reason.
Bug: ability to generate negative hash indexes leads to UB, which includes the above concern.
putValueForKey(hashArray, -100, "minus");
Should key
have a negative value, so will hashCode()
return a negative value which causes out-of-bounds array access.
Various ways to handle that. For me I would use an unsigned type like unsigned
or size_t
. Also amend calling code.
// int hashCode(int key) {
unsigned hashCode(int key) {
return (unsigned) key % SIZE;
}
Other idea:
Consider prime values for HASH
Certainly hashCode()
here is a simple function and not truly meant to hash the key
highly uniformly across [0...SIZE)
. Yet remember that if the hash calculation before the final % SIZE
is good, finding the remainder with any SIZE
will be good too.
On the other hand (OTOH), if the hash calculation before the % SIZE
is weak, using a prime for SIZE
will likely to make it better. Example
// #define SIZE 20
#define SIZE 23
unsigned hashCode(int key) {
return (unsigned) key % SIZE;
}
add a comment |
up vote
1
down vote
up vote
1
down vote
The while loops that might get into an infinite loop condition for some reason.
Bug: ability to generate negative hash indexes leads to UB, which includes the above concern.
putValueForKey(hashArray, -100, "minus");
Should key
have a negative value, so will hashCode()
return a negative value which causes out-of-bounds array access.
Various ways to handle that. For me I would use an unsigned type like unsigned
or size_t
. Also amend calling code.
// int hashCode(int key) {
unsigned hashCode(int key) {
return (unsigned) key % SIZE;
}
Other idea:
Consider prime values for HASH
Certainly hashCode()
here is a simple function and not truly meant to hash the key
highly uniformly across [0...SIZE)
. Yet remember that if the hash calculation before the final % SIZE
is good, finding the remainder with any SIZE
will be good too.
On the other hand (OTOH), if the hash calculation before the % SIZE
is weak, using a prime for SIZE
will likely to make it better. Example
// #define SIZE 20
#define SIZE 23
unsigned hashCode(int key) {
return (unsigned) key % SIZE;
}
The while loops that might get into an infinite loop condition for some reason.
Bug: ability to generate negative hash indexes leads to UB, which includes the above concern.
putValueForKey(hashArray, -100, "minus");
Should key
have a negative value, so will hashCode()
return a negative value which causes out-of-bounds array access.
Various ways to handle that. For me I would use an unsigned type like unsigned
or size_t
. Also amend calling code.
// int hashCode(int key) {
unsigned hashCode(int key) {
return (unsigned) key % SIZE;
}
Other idea:
Consider prime values for HASH
Certainly hashCode()
here is a simple function and not truly meant to hash the key
highly uniformly across [0...SIZE)
. Yet remember that if the hash calculation before the final % SIZE
is good, finding the remainder with any SIZE
will be good too.
On the other hand (OTOH), if the hash calculation before the % SIZE
is weak, using a prime for SIZE
will likely to make it better. Example
// #define SIZE 20
#define SIZE 23
unsigned hashCode(int key) {
return (unsigned) key % SIZE;
}
edited Aug 4 '17 at 17:22
answered Aug 4 '17 at 17:17
chux
12.5k11344
12.5k11344
add a comment |
add a comment |
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f171784%2fc-hashtable-implementation%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown