I took it upon myself to develop a concurrent generic hash table in C.
Relevant contents of hash_table.h:
typedef struct list_node {
void * data;
struct list_node * next;
} list_node_t;
typedef struct hash_table {
int max_size;
int count;
list_node_t * * elements;
pthread_rwlock_t * locks;
pthread_rwlock_t global_table_lock;
hash_table_compare_function compare;
hash_table_hash_function hash;
} hash_table_t;
Relevant contents of hash_table.c:
#define LOCK_RD(lock) pthread_rwlock_rdlock(&lock);
#define LOCK_WR(lock) pthread_rwlock_wrlock(&lock);
#define UNLOCK(lock) pthread_rwlock_unlock(&lock);
bool
hash_table_remove(hash_table_t * table, void * element)
{
int hash_value = table->hash(element);
list_node_t * node, * prev;
LOCK_WR(table->locks[hash_value]);
node = table->elements[hash_value];
prev = NULL;
while (node) {
if (!table->compare(node->data, element)) {
// value is first item in the list
if (node == table->elements[hash_value]) {
table->elements[hash_value] = node->next;
free(node);
UNLOCK(table->locks[hash_value]);
LOCK_WR(table->global_table_lock);
table->count--;
UNLOCK(table->global_table_lock);
return true;
} else {
// link previous node with one after current
prev->next = node->next;
free(node);
UNLOCK(table->locks[hash_value]);
LOCK_WR(table->global_table_lock);
table->count--;
UNLOCK(table->global_table_lock);
return true;
}
}
prev = node;
node = node->next;
}
UNLOCK(table->locks[hash_value]);
return false;
}
I wrote a test case which uses strings, in which this is the relevant code:
#include "hashtable.h"
#define NUM_THREADS 2
#define NUM_STRINGS 154560
#define NUM_LOOKUPS 10000
void *
do_work(void * data)
{
int thread_id = *(int*)data;
// write "threadX.txt" to filename, where X is the given thread id
char filename[64];
strcpy(filename, "thread");
char thread_id_str[4];
sprintf(thread_id_str, "%d", thread_id);
strcat(filename, thread_id_str);
strcat(filename, ".txt");
FILE * file = fopen(filename, "r");
char buffer[128];
int i, num_str_per_thread = NUM_STRINGS / NUM_THREADS;
char * str_array[num_str_per_thread];
for (i = 0; i < num_str_per_thread; i++) {
fgets(buffer, 128, file);
str_array[i] = calloc((strlen(buffer) + 1), sizeof(char));
strcpy(str_array[i], buffer);
}
fclose(file);
for (i = 0; i < num_str_per_thread; i++)
hash_table_insert(table, str_array[i]);
for (i = 0; i < NUM_LOOKUPS; i++)
hash_table_contains(table, str_array[rand() % num_str_per_thread]);
for (i = 0; i < num_str_per_thread / 2; i++)
hash_table_remove(table, str_array[rand() % num_str_per_thread]);
//sleep(2); NOTE: no read errors reported if I leave this sleep() here.
for (i = 0; i < num_str_per_thread; i++)
if (str_array[i])
free(str_array[i]);
return NULL;
}
void
create_workers()
{
pthread_t threads[NUM_THREADS];
int ids[NUM_THREADS];
int i;
for (i = 0; i < NUM_THREADS; i++)
ids[i] = i + 1;
for (i = 0; i < NUM_THREADS; i++)
pthread_create(&threads[i], NULL, do_work, (void*)&ids[i]);
for (i = 0; i < NUM_THREADS; i++)
pthread_join(threads[i], NULL);
}
The test case is supposed to work as follows: there are two files, thread1.txt and thread2.txt, each containing unique strings I have generated beforehand. I create two threads, and each will read from a file and store each string on an array of strings called str_array. They will then insert all these strings into the hash table and perform random searches (hash_table_contains) and deletions (hash_table_remove). Then, each will free their respective array of strings. However, when I run this test case, Valgrind reports the following:
Please note that there are no memory leaks. What I get from these errors is that a thread is, upon calling hash_table_remove, attempting to free memory already freed by free(str_array[i]). However, that makes no sense, since hash_table_remove is called before free(str_array[i]. I can't figure out what's giving me these invalid reads.
Thank you in advance!
Here, your thread removes at most half the strings it inserted:
for (i = 0; i < num_str_per_thread / 2; i++)
hash_table_remove(table, str_array[rand() % num_str_per_thread]);
(in fact, it is most likely to remove about 39% of the strings it inserted).
Then, it goes on to free all the strings it inserted:
for (i = 0; i < num_str_per_thread; i++)
if (str_array[i])
free(str_array[i]);
However, at least half (and most likely ~61%) of those strings are still in the hash table, where the other threads will try to compare them as they scan through the chained hash bucket entries. That's your use-after-free error.
Instead of freeing all the strings, you could free them as you remove them:
for (i = 0; i < num_str_per_thread / 2; i++)
{
int str_index = rand() % num_str_per_thread;
if (str_array[str_index])
{
hash_table_remove(table, str_array[str_index]);
free(str_array[str_index]);
str_array[str_index] = NULL;
}
}
At this point, the non-NULL entries in str_array[] are the strings still present in the hash table. You can't free them until they're removed from the hash table (or the hash table is no longer in use).
The fact that your test case got this wrong is a good indicator that the ergonomics of your interface are not as good as they could be. You should probably consider a design in which the ownership of the strings inserted is transferred to the hash table, so that hash_table_remove() is itself responsible for freeing the string.
Related
I'm doing a problem on Leetcode. It says I passed my first test case but I failed my second with a simple wrong answer. When I ran that case manually, I passed, so I know it's something to do with data persistence between their runs. It's obviously the hash table I'm using, which I first declared globally. To fix, I first tried resetting to zero with calloc and memset but I got segfault errors.
I decided the logical thing is to create the hash inside the caller function, so each time it's called it would be reinitalized. However, I am still getting seg fault errors (address sanitizer deadly signal) even though it seems like it should work. Am I misusing pointers?
Here is my code that's not working. The comments //\ represent lines of code I had before, where it did work case by case. There were not many changes- mostly to return the hash. I comment where the seg fault occurs (per my printf debugging). I hope I didn't miss anything. I'm trying to display two coding instanecs in one because it's mostly reduntant.
#define SIZE 1000
typedef struct htent{
struct htent* next;
int key, value;
} htent;
int hashf(int val, int size){
return val % SIZE; // don't need anything fancy
}
htent* create_new_htent(int val){ // the value is its own key
htent* ent = malloc(sizeof(htent));
ent->next = NULL;
ent->key = val;
ent->value = val;
return ent;
}
// htent* ht[SIZE] = {0}; //\\ This was my method before (main difference)
htent* build_hash_table_n_get_lowest(int* nums, int numsSize, int* low){ //\\ returned void before
htent* ht[SIZE] = {0}; //\\ before was commented out
int hash;
int running_low = 10000; // large #
htent* tmp;
for (int i = 0; i < numsSize; i++){
if (nums[i] < 1 ) continue; // drop negatives
if (nums[i] < running_low) running_low = nums[i];
printf("%d: Running Low %d\n", i, running_low);
htent* ent = create_new_htent(nums[i]);
hash = hashf(nums[i], numsSize);
if (ht[hash] == NULL){
ht[hash] = ent; // singly linked list
continue;
}
tmp = ht[hash];
while(tmp->next != NULL){
tmp = tmp->next;
}
tmp->next = ent;
}
*low = running_low;
return ht; //\\ was commented out before
}
int find_missing_lowest(htent** ht, int numsSize, int lowest){
int moving_target = 1;
for (int i = 0; i<numsSize; i++){
if (ht[moving_target] == NULL){ // SEG FAULTS HERE!!!
return moving_target;
}
moving_target++;
}
return moving_target;
}
int firstMissingPositive(int* nums, int numsSize){ // Function Leetcode calls
int lowest;
htent* ht = build_hash_table_n_get_lowest(nums, numsSize, &lowest); // tried with htent**
return find_missing_lowest(ht, numsSize, lowest);
}
EDIT: I built this function using the global scope approach and it works and these tests pass (just have to handle edge cases), but there has to be a better way;
void refresh_hashtable(){
for (int i = 0; i<SIZE; i++){
ht[i] = NULL;
}
}
I have a program that reads a file with two columns of numbers, sorts them, creates three tables, one with only the nodes (individually), one with all the edges and one that has the amount of edges for every node. The problem is that when I try to print the edges, it prints them wrong or it says it cannot find them. Through some gdb I found out that the first arrays are fine but the third stores a bunch of random numbers (or zeros) through the end. Any help would be appreciated.
The file looks like this (start/end node for each edge):
7856 8192
7754 7005
7862 1982
7862 3293
7862 4037
7862 5210
7862 5605
7862 7860
The code looks like this:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int mapcmp(const void *a,const void *b){
return ( *(int*)a - *(int*)b );
}
int mapdoublesize(int** map,int nodes){
int* new_array=malloc(nodes*2*sizeof(int));
if(new_array==NULL){
printf("Error allocating memory\n");
abort();
}
nodes*=2;
for(int i=0;i<nodes;i++){
new_array[i]=(*map)[i];
}
free(*map);
*map=new_array;
return nodes;
}
typedef struct {
int start;
int end;
} path;
int cmp(const void *a,const void *b){
int l=((path*)a)->start;
int r=((path*)b)->start;
if(l>r)
return 1;
if(l<r)
return -1;
if(l==r)
return 0;
}
int doublesize(path** array,int n){
path* new_array=malloc(n*2*sizeof(path));
if(new_array==NULL){
printf("Error allocating memory\n");
abort();
}
for(int i=0;i<n;i++){
new_array[i]=(*array)[i];
}
free(*array);
*array=new_array;
n*=2;
return n;
}
int main()
{
int maxsize=10;
int test;
path* array=malloc(maxsize*sizeof(path));
if(array==NULL) {
printf("Error allocating memory\n");
abort();
}
FILE* fd=fopen("Wiki-Vote.txt","r");
if(fd==NULL) {
printf("Error opening file\n");
abort();
}
char buff[200];
int counter=0;
char c;
while(fgets(buff,200,fd)) {
c=buff[0];
if(c=='#') {
continue;
}
sscanf(buff,"%d%d",&array[counter].start,&array[counter].end);
counter++;
if(counter==maxsize){
maxsize=doublesize(&array,maxsize);
}
}
int i;
maxsize=counter;
counter=0;
qsort(&array[0],maxsize,sizeof(path),cmp);
counter=0;
int nodes=10;
int* map=malloc(nodes*sizeof(int));
if(map==NULL){
printf("Error allocating memory\n");
abort();
}
for(i=0;i<maxsize;i++){
if(map[counter-1]==array[i].start)
continue;
map[counter]=array[i].start;
counter++;
if(counter==nodes){
nodes=mapdoublesize(&map,nodes);
}
}
int j;
for(i=0;i<maxsize;i++){
for(j=0;j<counter;j++){
if(map[j]==array[i].end)
break;
}
if(j!=counter)
continue;
map[counter]=array[i].end;
counter++;
if(counter==nodes)
nodes=mapdoublesize(&map,nodes);
}
nodes=counter;
qsort(&map[0],nodes,sizeof(int),mapcmp);
int* arraynodes=malloc(nodes*sizeof(int));
int* arrayedges=malloc(maxsize*sizeof(int));
if(arraynodes==NULL||arrayedges==NULL){
printf("Error allocating memory\n");
abort();
}
counter=1;
arraynodes[0]=0;
for(i=0;i<maxsize;i++){
arrayedges[i]=array[i].end;
if(array[i].start!=array[i+1].start){
arraynodes[counter]=i;
counter++;
}
}
int x;
printf("give number to search: ");
scanf("%d",&x);
for(i=0;i<nodes;i++){
if(x==map[i]){
printf("found \n");
break;
}
}
if(i==nodes){
printf("not found \n");
abort();
}
for(j=arraynodes[i];j<arraynodes[i+1];j++){
printf("%d\n",arrayedges[j]);
}
free(arraynodes);
free(arrayedges);
free(map);
fclose(fd);
free(array);
return 0;
}
Core answer:
As I understand your intention, you want arraynodes to hold for each node index the offset in the edge list where the edges for that node start.
You iterate over the edge list and every time the starting point changes, you store the current offset in arraynodes. This is flawed, because not all nodes are the starting point of an edge. So if your edge list has an edge from node 5 -> 7 and then an edge from 6 -> 7 then you will register the change of the starting point from 5 to 6, but you will store the current offset at the beginning of arraynodes and not for the 5th node.
To fix this, instead do this: Keep an offset into the edge list, initially zero. Iterate over the nodes, for each node store the current offset into arraynodes. Then increment the offset as long as the starting point of the edge at the current offset is equal to the current node. This way arraynodes will tell you for each node index, at which index in the edge list the edges starting at this node are stored.
/**
* Assumption: Edges are sorted by their starting point.
*/
int edge_count = maxsize;
int edge_offset = 0;
/**
* For each node:
*
* - Store current edge_offset in arraynodes
* - Increment edge_offset as long as the start point
* of the edge at that offset matches the current node.
*/
for (int i = 0; i < nodes; i++) {
int current_node = map[i];
arraynodes[i] = edge_offset;
while (edge_offset < edge_count && array[edge_offset].start == current_node) {
edge_offset++;
}
}
/**
* Copy end-points of edges to arrayedges.
*
* You don't really need this, you could also directly
* access the end-points in your output loop ...
*/
for (int i = 0; i < edge_count; i++) {
arrayedges[i] = array[i].end;
}
Memory safety issues:
There are a several memory safety issues in your code:
Buffer underflow: In the first pass of the loop, counter is zero, so map[counter-1] goes out of bounds.
counter = 0;
int nodes = 10;
int *map = malloc(nodes * sizeof(int));
if (map == NULL) {
printf("Error allocating memory\n");
abort();
}
for (i = 0; i < maxsize; i++) {
if (map[counter - 1] == array[i].start)
continue;
Buffer overflow: When you initialize the map, you want to double its size when it's full. However, in mapdoublesize when you copy the data from the old map to the new map, you iterate over the whole new map, so the second half of this loop reads past the bounds of the old map:
nodes *= 2;
for (int i = 0; i < nodes; i++) {
new_array[i] = (*map)[i];
}
Buffer overflow: In the last iteration of this loop: The access to array[i+1] is out of bounds:
for (i = 0; i < maxsize; i++) {
arrayedges[i] = array[i].end;
if (array[i].start != array[i + 1].start) {
Buffer overflow: In your output loop, if i is the last node, your access to arraynodes[i+1] goes out of bounds:
for (j = arraynodes[i]; j < arraynodes[i + 1]; j++) {
I do not guarantee that I found all memory safety issues. There might very well be more. I would advice you to improve the structuring and documentation of your program: Break down your program into smaller functions that do one step and document the assumptions and preconditions of this step (i.e. what are the bound of the array you are accessing?). Give the variables names that cleary describe their purpose, do not reuse variables. This should make it easier for you to spot these kinds of errors. Also I would advice you to use tools to check for memory safety issues. GCC and Clang both have a feature called ASAN that will automatically insert debug code into your binary that will detect and report memory safety issues when you run your program. You can enable this by compiling with -fsanitize=address (reference). Another tool with a similar scope would be Valgrind (reference). These programs cannot find all errors of course, since they only do dynamic analysis of the code that is actually executed. If there is a bug in some branch of your program that is not reached by the current execution, it will not be detected. So you still do not get around taking a careful look at your program.
I want to know about bring the text file with 10 names and read it. 10 names are sorting by descending and forming a hash table with division method. I need to construct linked list of them. The hash table's index is number of 7.
I've tried on match pointer variable and made a hash table, but I can't do that. I'm in trouble with making hash table, inserting data, printing hash table and searching data(A function to find when I type a name.). I need to add more function..how do i made it?
#define SIZE 7
struct node {
char data[100][20];
struct node* next;
};
struct index {
struct node* head;
int count;
};
struct sum (data){
struct node* ptr;
int sum,i;
for (i=0; i<20; i++) {
ptr -> data[i] = ptr;
strcpy(sum,ptr);
}
return sum;
};
int hashFunction (int sum) {
return sum%SIZE;
}
void descend (data) {
int temp;
for(i=0;i<100;i++) {
for(j=0;j=20;j++) {
if (data[i][j+1]>data[i][j])
temp=data[i][j];
data[i][j]=data[i][j+1];
data[i][j+1]=temp;
}
}
}
int main (void) {
char data[100][20];
FILE *fp;
fp = fopen("data.txt","r");
for (int i=0; i<20; i++)
fscanf (fp,"%s",&data);
printf("%s\n",data);
}
fclose(fp);
hashTable = (struct index*)malloc(SIZE*sizeof(struct index));
descend(data);
return 0;
}
There are lot of bugs in the code, I'm just putting my possible observation. Firstly this
fscanf (fp,"%s",&data);
should be
fscanf (fp,"%s",&data[i]);
Secondly, here in descend() function inner loop condition part you are using j=20 which loops to run infinitely. This is where MACRO comes handy as this j=20 simply runs i.e if it could have ROW=j where ROW is 20 compiler produces meaningful error. This
void descend (data) { /* what is the tyep of data ? you should mention the data type */
int temp;
for(i=0;i<100;i++) { /* there are only 20 lines not 100 i.e it should be i<20 */
for(j=0;j=20;j++) { /* condition is wrong, you indented for j<20 but that too
wrong as there are supposed to be max 100 char in line
it should be j<100 */
if (data[i][j+1]>data[i][j]) /* condition is not correct */
temp=data[i][j];
data[i][j]=data[i][j+1];
data[i][j+1]=temp;
}
}
}
Correct version descend function can be
void descend (char (*data)[ROW], int col) { /* define ROW as macro with value 20 and pass the col i.e 100 */
int temp;
for(i=0;i < ROW; i++) {
for(j=0;j < col; j++) {
if (data[i][j] > data[i][j+1])
temp = data[i][j];
data[i][j] = data[i][j+1];
data[i][j+1] = temp;
}
}
}
Also check the return value of fopen() to check whether it was success r failed and do proper validation. For e.g
fp = fopen("data.txt","r");
if(fp == NULL) {
/* #TODO error handling */
fprintf(stderr,"file doesn't exist");
return 0;
}
To begin with, it looks like you should declare char data[20][100] instead of char data[100][20].
Then, inside the loop of 20 iterations, you should refer to data[i] instead of data:
for (int i=0; i<20; i++)
fscanf(fp,"%s",data[i]);
printf("%s\n",data[i]);
}
Keep in mind you're assuming that each line in your input file is at most 99-character long.
This doesn't answer the actual question I suppose, but you should by the least get all the above fixed.
I got assigment from the professor to create a list using pointers. I'm having a problem with free() in all of my functions when I try to free memory. I keep getting a message:
"HEAP CORRUPTION DETECTED: after Normal block (#83) at 0x00D58CE0.
CRT detected that the application wrote to memory after end of heap buffer."
I have no idea how to fix it. I've tried different things but nothing worked so far. I'm not sure where to search for it either anymore. I know that my program is not properly secured yet, but it doesn't affect the problem. I'm trying to eliminate it first before going further with it. Also, do you have any advice on detecting memory leaks from the program in visual studio? Here are my functions, full source code is below in the link:
Full Source Code
struct ListElement
{
int value;
struct element *next;
};
typedef struct ListElement List;
typedef List *ListEl;
void ViewListBackwards(ListEl *list_el)
{
ListEl current_element = *list_el;
int size = 0;
int i = 0;
int *reversed_array;
while (current_element->next != NULL)
{
size++;
current_element = current_element->next;
}
current_element = *list_el;
reversed_array = (int*)malloc(size * sizeof(*reversed_array));
for (i = size; i >= 0; i--)
{
reversed_array[i] = current_element->value;
current_element = current_element->next;
}
for (i = 0; i <= size; i++)
{
printf(" %d. %d\n", i + 1, reversed_array[i]);
}
free(reversed_array);
}
void RemoveFromListFront(ListEl *list_el)
{
if (ListEmpty(list_el) == 0)
{
ListEl current_element = *list_el;
*list_el = current_element->next;
free(current_element);
}
else
{
printf("List is empty!\n");
}
}
void RemoveFromListBack(ListEl *list_el)
{
if (ListEmpty(list_el) == 0)
{
ListEl current_element = *list_el;
ListEl last_element = *list_el;
while (current_element->next != NULL)
{
last_element = current_element;
current_element = current_element->next;
}
last_element->next = NULL;
free(current_element);
}
}
In the following code:
reversed_array = (int*)malloc(size * sizeof(*reversed_array));
for (i = size; i >= 0; i--)
{
reversed_array[i] = current_element->value;
current_element = current_element->next;
}
for (i = 0; i <= size; i++)
{
printf(" %d. %d\n", i + 1, reversed_array[i]);
}
you are allocating (what amounts to) an int reversed_array[size] array, but then proceed to write to reversed_array[size], which is sizeof(int) past the end of the allocated memory segment.
You instead want to change your for loops to:
for (i = size - 1; i >= 0; i--)
so that the indices you write to start at reversed_array[size - 1].
EDIT:
As an aside, please, as other people have suggested in the comments, don't cast malloc(). This is unneeded in C, as void * can be assigned to any object pointer type variables, and in addition can hide bugs in your code, such as forgetting to include stdlib.h.
And FWIW, you don't need the parentheses around sizeof(*reversed_array), as parentheses are only needed when applying the sizeof operator to types.
This is mostly just a style suggestion; it is preferred by many (myself included) that you omit the extraneous parentheses and write that expression as sizeof *reversed_array, as that makes it clear that sizeof is a unary operator and not a function.
In my below code I am trying to create a dynamically expandable array of memory.
#include <stdio.h>
#include <stdlib.h>
#define BLOCKSIZE 5
int hash_table_length = 0;
int *currentblock = NULL;
int size_left;
int *hash_table = NULL;
int *start = NULL;
int *create_hash_table() {
int *tmp;
if (currentblock == NULL || size_left == 0) {
if (currentblock == NULL) {
currentblock = (int *) malloc( BLOCKSIZE * sizeof(int));
start = currentblock;
size_left = BLOCKSIZE;
} else {
currentblock = (int *) malloc( BLOCKSIZE * sizeof(int));
size_left = BLOCKSIZE;
}
}
tmp = currentblock++;
size_left -= 1;
return tmp;
}
void build() {
int hash;
int i = 0;
for (i = 0; i < 20; i++) {
hash = i + 3;
if (hash_table_length == 0) {
hash_table = create_hash_table();
hash_table_length++;
} else {
hash_table = create_hash_table();
hash_table_length++;
}
hash_table = &hash;
printf("hash value is %d\n", *hash_table);
}
}
int main() {
build();
// How do I reach the start of the hash table again?
// the below start does not give me the first value
printf("Hash table first value is %d\n", *start);
return 0;
}
My problem here is I wish to traverse through the values stored in the hash_table. I am unable to reach to the first element/address of the hash_table. I wish to print out all the values stored in my hash table. How can this be done?
In your code the hash values never get stored inside the hash table(inside currentblock). Inside the create_hash_table() function you allocate memory for a new block but never store values inside this block. Thus if you try dereferencing any of these int* locations you might get a garbage value(which may be a 0).
This is what is precisely happening inside your main() function when you dereference the start pointer. It is infact pointing to the start of the hash table and as that location is uninitialized it gives an output of 0.
To actually store values inside the hash table change the following inside build():
hash_table = &hash;
to:
*hash_table = hash; // Store value of 'hash' inside the memory location pointed to by hash table(which happens to be 'current_block' inside build())
Now if you try running the code, it will output 3.
Coming to the second part of question as to how you'll traverse the entire hash table: It cannot be done using this code. This is because there is no linkage between your malloc'd blocks of integers. The malloc() call can assign any block of free memory from the heap. Thus in the current form you have disconnected blocks of locations which cannot be traversed.
Instead of malloc you can use realloc to increase the size of your current block. realloc allocates memory for the larger block and copies your previous data to this new block. This will essentially allow you to traverse the entire hash table using start.
Here is how you might do that:
#include <stdio.h>
#include <stdlib.h>
#define BLOCKSIZE 5
int hash_table_length = 0;
int *currentblock = NULL;
int size_left;
int *hash_table = NULL;
int *start = NULL;
int *create_hash_table() {
int *tmp;
if (currentblock == NULL || size_left == 0) {
if (currentblock == NULL) {
currentblock = (int *) malloc(BLOCKSIZE * sizeof(int));
start = currentblock;
size_left = BLOCKSIZE;
} else {
/* Call realloc() to allocate new memory block of size (hash_table_length+BLOCKSIZE) and copy previous data*/
currentblock = ((int *) realloc(start,(hash_table_length + BLOCKSIZE) * sizeof(int))) + hash_table_length;
size_left = BLOCKSIZE;
}
}
tmp = currentblock++;
size_left -= 1;
return tmp;
}
void build() {
int hash;
int i = 0;
for (i = 0; i < 20; i++) {
hash = i + 3;
if (hash_table_length == 0) {
hash_table = create_hash_table();
hash_table_length++;
} else {
hash_table = create_hash_table();
hash_table_length++;
}
/* Store value of hash inside the hash_table */
*hash_table = hash;
printf("hash value is %d\n", *hash_table);
}
}
int main() {
int i;
build();
printf("Hash table first value is %d\n", *start);
/* Traverse the hash table */
for(i = 0; i < hash_table_length; ++i)
printf("hash_table[%d] = %d\n",i,*start++);
return 0;
}