Segfault when reading from update linked list - c

So I'm working on an implementation of a hash table. I'm not very experienced with C and pointers and getting a bit stuck.
I've got hashtable definition that looks like this:
typedef struct KVnode {
int kv[2];
struct KVnode *next;
} KVnode;
typedef struct hashtable {
int size; // size of hash table
int entries; // number of slots allocated in table
KVnode *table; /* pointer to table. Each entry will point to linked list
of key-value nodes */
} hashtable;
Where the hash table struct contains a table of KVnode pointers. The KVnodes are essentially a linked list to store collisions.
My implementation of put looks like this:
void put(hashtable* ht, keyType key, valType value){
int index = hash_key(key, ht->size);
KVnode *new_node = malloc( sizeof(KVnode) );
new_node->kv[0] = key;
new_node->kv[1] = value;
new_node->next = NULL;
printf("Inserting at index: %i, key:%i, val:%i \n", index, key, value);
// If next val is 0, we can set the first node to key-value
if( ht->table[index].next == 0)
ht->table[index] = *new_node;
else{ // find last node in linked list and append new_node
KVnode cn = ht->table[index];
while( cn.next != NULL )
cn = *cn.next;
cn.next = new_node;
}
}
And here the overall code:
#include <stdio.h>
#include <stdlib.h>
#define SIZE 503
typedef struct KVnode {
int kv[2];
struct KVnode *next;
} KVnode;
typedef struct hashtable {
int size; // size of hash table
int entries; // number of slots allocated in table
KVnode *table; /* pointer to table. Each entry will point to linked list
of key-value nodes */
} hashtable;
typedef int keyType;
typedef int valType;
void init(hashtable**);
int hash_key(keyType key, int size);
void put(hashtable* ht, keyType key, valType value);
void init(hashtable** ht) {
*ht = (hashtable *) malloc( sizeof(hashtable) );
if(*ht == NULL){
printf( "Error: Unable to allocate memory for hashtable" );
exit(1);
}
else{
(*ht)->entries = 0;
(*ht)->size = SIZE;
(*ht)->table = calloc((*ht)->size , sizeof(KVnode *));
}
}
int hash_key(keyType key, int size){
return key % size;
}
void put(hashtable* ht, keyType key, valType value){
int index = hash_key(key, ht->size);
KVnode *new_node = malloc( sizeof(KVnode) );
new_node->kv[0] = key;
new_node->kv[1] = value;
new_node->next = NULL;
printf("Inserting at index: %i, key:%i, val:%i \n", index, key, value);
// If next val is 0, we can set the first node to key-value
if( ht->table[index].next == 0)
ht->table[index] = *new_node;
else{ // find last node in linked list and append new_node
KVnode cn = ht->table[index];
while( cn.next != NULL )
cn = *cn.next;
cn.next = new_node;
}
}
int main(){
hashtable *t = NULL;
init(&t);
put(t, 225, 100);
put(t, 55555, 100);
printf("node at 255, k:%i, v:%i\n", t->table[225].kv[0],t->table[225].kv[1] );
printf("node at 255, 2nd node, k:%i, v:%i\n",
t->table[225].next->kv[0],t->table[225].next->kv[1] );
free(t);
}
The program compiles fine but at runtime i get at segfault. Here's the output:
ds-MacBook-Pro:project0 d$ ./ll
Inserting at index: 225, key:225, val:100
Inserting at index: 225, key:55555, val:100
node at 255, k:55555, v:100
Segmentation fault: 11
I can't figure out if the issue is my print statement or if I'm actually not appending the linked list correctly in put.

Related

Rehashing hashtable

When I debug I find out that in the temporary hashtable - 'hashtable2' no elements are inserted. All other parts of the hashtable code work.
I am not sure why the no values get inserted into hashtable2
void rehash(struct HashTable* hashTable, int keySize)
{
int i;
struct HashElement* current;
int i2 = 0;
struct List* word;
struct List* key;
struct HashTable* hashTable2 = hashTableConstructor(33524, keySize);
hashTable->keySize = keySize;
for(i=0; i<hashTable->numBuckets; i++) //for every bucket
{
current = hashTable->buckets[i];
//'walk' the linked list of HashElements in the bucket
while( current != NULL )
{ //till the end of hash table
word = listConstructor();
key = listConstructor();
word = current->value;
for(i2 = 0; i2<keySize; i2++)
{
listAdd(key, tolower(getCharacter(word,i2)));
}
insert(hashTable2, word, key);
listDestructor(key);
listDestructor(word);
current = current->next;
}
}
hashTableDestructor(hashTable);
hashTable = hashTableConstructor(33524, keySize);
hashTable = hashTable2;
}

All Nodes in a linked list point to same object

The problem is somewhere in here....
char buffer[80];
char *name;
while (1) {
fgets(buffer, 80, inf); //reads in at most 80 char from a line
if (feof(inf)) //this checks to see if the special EOF was read
break; //if so, break out of while and continue with your main
name = (char *) malloc(sizeof(char)*20);
....
name = strtok(buffer, " ");//get first token up to space
stock = newStock(name,...)
....
}
I'm working in C with generic linked lists. I made a list implementation that I've tested and know works with chars. I'm trying to add stocks (I created a stock struct) to the linked list, with each node of the linked list holding a stock struct, but when I finish reading in the stocks all of the nodes point to the same struct and I can't figure out why. Here's some snippets of my code
list *list = malloc(sizeof(list));
newList(list, sizeof(stock_t));
while(1) {
...
(read from file)
...
stock_t *stock;
stock = newStock(name, closes, opens, numshares, getPriceF, getTotalDollarAmountF,getPercentChangeF,toStringF);
addToBack(list, stock);
}
Here's the newStock function:
stock_t *newStock(char *name, float closingSharePrice, float openingSharePrice, int numberOfShares, getPrice getP, getTotalDollarAmount getTotal, getPercentChange getPercent, toString toStr) {
stock_t *stock = malloc(sizeof(stock));
stock->stockSymbol = name;
stock->closingSharePrice = closingSharePrice;
stock->openingSharePrice = openingSharePrice;
stock->numberOfShares = numberOfShares;
stock->getP = getP;
stock->getTotal = getTotal;
stock->getPercent = getPercent;
stock->toStr = toStr;
return stock;
}
In a way I see what's wrong. newStock returns a new pointer every time, but it always gets stored in the variable 'stock' which is what every node points to, so it's going to be equal to whatever the last pointer newStock returned was...but I don't see the way around this. I tried having newStock return just a stock_t, and doing addToBack(list, &stock), but that didn't solve the problem either.
Any help would be appreciated!
Here is some code from the list:
typedef struct node {
void *data;
struct node *next;
}node_t;
typedef struct {
int length;
int elementSize;
node_t *head;
node_t *tail;
} list;
void newList(list *list, int elementSize) {
assert(elementSize > 0);
list->length = 0;
list->elementSize = elementSize;
list->head = list->tail = NULL;
}
void addToBack(list *list, void *element) {
node_t *node = malloc(sizeof(node_t));
node->data = malloc(list->elementSize);
node->next = NULL; //back node
memcpy(node->data, element, list->elementSize);
if (list->length == 0) { //if first node added
list->head = list->tail = node;
}
else {
list->tail->next = node;
list->tail = node;
}
list->length++;
}
Here's code from the stock struct:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
typedef float (*getPrice)(void *S);
typedef float (*getTotalDollarAmount)(void *S);
typedef float (*getPercentChange)(void *S);
typedef char *(*toString)(void *S);
typedef struct stock{
char *stockSymbol;
float closingSharePrice;
float openingSharePrice;
int numberOfShares;
getPrice getP;
getTotalDollarAmount getTotal;
getPercentChange getPercent;
toString toStr;
}stock_t;
The generic functions probably seem like overkill but this is for homework (if you couldn't tell already) so we were asked to specifically use them. I don't think that has anything to do with the problem though.
Here are the definitions for those functions anyway
float getPriceF(void *S) {
stock_t *stock = (stock_t*)S;
return stock->closingSharePrice;
}
float getTotalDollarAmountF(void *S) {
stock_t *stock = (stock_t*)S;
return ((stock->closingSharePrice) * (stock->numberOfShares));
}
float getPercentChangeF(void *S) {
stock_t *stock = (stock_t*)S;
return ((stock->closingSharePrice - stock->openingSharePrice)/(stock->openingSharePrice));
}
char *toStringF(void *S) {
stock_t* stock = (stock_t*)S;
char *name = malloc(20*sizeof(char));
//sprintf(name, "Symbol is: %s. ", (stock->stockSymbol));
return stock->stockSymbol;
}
void printStock(void *S) {
char *str = toStringF(S);
printf("%s \n", str);
}
And this is how I'm traversing the list:
typedef void (*iterate)(void *); //this is in the list.h file, just putting it here to avoid confusion
void traverse(list *list, iterate iterator) {
assert(iterator != NULL);
node_t *current = list->head;
while (current != NULL) {
iterator(current->data);
current = current->next;
}
}
And then in my main I just called
traverse(list, printStock);
I can't find any problems with your code (that would cause your problem, anyway - there are places where you don't check the return from malloc() and stuff like that, but those are not relevant to this question). You don't supply the definition of stock_t, so I made a new data struct, and a new couple of functions, otherwise I just copied and pasted the code you provided:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
/* Your code starts here */
typedef struct node {
void *data;
struct node *next;
}node_t;
typedef struct {
int length;
int elementSize;
node_t *head;
node_t *tail;
} list;
void newList(list *list, int elementSize) {
assert(elementSize > 0);
list->length = 0;
list->elementSize = elementSize;
list->head = list->tail = NULL;
}
void addToBack(list *list, void *element) {
node_t *node = malloc(sizeof(node_t));
node->data = malloc(list->elementSize);
node->next = NULL; //back node
memcpy(node->data, element, list->elementSize);
if (list->length == 0) { //if first node added
list->head = list->tail = node;
}
else {
list->tail->next = node;
list->tail = node;
}
list->length++;
}
/* Your code ends here */
/* I made a new struct, rather than stock, since you didn't supply it */
struct mydata {
int num1;
int num2;
};
/* I use this instead of newStock(), but it works the same way */
struct mydata * newNode(const int a, const int b) {
struct mydata * newdata = malloc(sizeof *newdata);
if ( newdata == NULL ) {
fputs("Error allocating memory", stderr);
exit(EXIT_FAILURE);
}
newdata->num1 = a;
newdata->num2 = b;
return newdata;
}
/* I added this function to check the list is good */
void printList(list * list) {
struct node * node = list->head;
int n = 1;
while ( node ) {
struct mydata * data = node->data;
printf("%d: %d %d\n", n++, data->num1, data->num2);
node = node->next;
}
}
/* Main function */
int main(void) {
list *list = malloc(sizeof(list));
newList(list, sizeof(struct mydata));
struct mydata * data;
data = newNode(1, 2);
addToBack(list, data);
data = newNode(3, 4);
addToBack(list, data);
data = newNode(5, 6);
addToBack(list, data);
printList(list);
return 0;
}
which outputs this:
paul#MacBook:~/Documents/src$ ./list
1: 1 2
2: 3 4
3: 5 6
paul#MacBook:~/Documents/src$
demonstrating that you have a 3 node list, with all nodes different and where you'd expect them to be.
Either there is some other problem in code you're not showing, or for some reason you are thinking each node points to the same struct when it actually doesn't.
One possibility is that you have a char * data member in your stock struct. It's impossible to tell from the code you provided, but it's possible that you really are creating different nodes, but they all end up pointing to the same name, so they just look like they're the same. If you're assigning a pointer to name, you should make sure it's freshly allocated memory each time, and that you're not just, for instance, strcpy()ing into the same memory and assigning the same address to each stock struct.
EDIT: Looks like that was your problem. This:
name = (char *) malloc(sizeof(char)*20);
....
name = strtok(buffer, " ");
should be:
name = (char *) malloc(sizeof(char)*20);
....
strcpy(name, strtok(buffer, " "));
Right now, you malloc() new memory and store a reference to it in name, but then you lose that reference and your memory when you overwrite it with the address returned from strtok(). Instead, you need to copy that token into your newly allocated memory, as shown.

How to get entire contents of binary tree in array form?

I have a C structure that represents a binary tree:
struct btree {
char *word;
int frequency;
struct btree *left;
struct btree *right;
};
I want to create a function btree_list(struct btree*) that returns an array of all the btree objects in the binary tree passed to it. Order does not matter.
Here is an example of how this function would work:
struct btree *T = populate_with_random_values();
struct btree *entries = (struct btree*) malloc(sizeof(struct btree) * btree_size(T));
entries = btree_list(T);
while (*entries != NULL) {
printf("There are %d occurences of the word %s", entries->frequency, entries->word);
entries++;
}
Also for each element E in entries, E->left and E->right should be set to NULL since they aren't technically being used. How would I go about implementing this?
This could be the array:
typedef struct {
struct btree **data;
size_t count;
} t_tbl;
t_tbl *tbl_create(size_t count)
{
t_tbl *new = NULL;
if (count > 0) {
new = malloc(sizeof(t_tbl));
new->data = malloc(count * sizeof(struct btree *));
new->count = 0;
}
return new;
}
void tbl_destroy(t_tbl *table)
{
if (table) {
free(table->data);
free(table);
}
}
And this could be the process:
void btree_populate_array(const t_node *root, t_tbl *table)
{
if (root->left) btree_populate_array(root->left, table);
table->data[table->count++] = root;
if (root->right) btree_populate_array(root->right, table);
}
if (root) {
t_tbl *table = tbl_create(btree_size);
btree_populate_array(root, table);
/* Do stuff with array */
tbl_destroy(table);
}
You have to check malloc, if you don't know the size of btree:
void btree_count(const t_node *root, size_t *count)
{
if (root->left) btree_count(root->left, count);
(*count)++;
if (root->right) btree_count(root->right, count);
}
size_t btree_size = 0;
if (root) {
btree_count(root, &btree_size);
}
Rather than returning the array just pass its base address to make your life easier and return the count of your array:
int TraverseTree(int arr[],btreenode *root, int depth)
{
static int count = 0;
if (root)
{
count++;
TraverseTree(arr,root->right,depth+1);
arr[depth]=root->data;
TraverseTree(arr,root->left, depth+1);
}
return count;
}
Well, do you want a preorder, inorder, or postorder traversal?
Here's a preorder example in pseudocode: (credit to Wikipedia)
iterativePreorder(node)
parentStack = empty stack
while not parentStack.isEmpty() or node ≠ null
if node ≠ null then
visit(node)
if node.right ≠ null then
parentStack.push(node.right)
node = node.left
else
node = parentStack.pop()
You'll have to tweak this a bit in order to get it to return a list of the nodes, but the idea behind walking the tree is all there.

Sequence insert at arbitrary index implemented with Linked list

I'm trying to implement sequence_insert_at using the add_to_front function here
Everything before
typedef struct sequence *Sequence;
is pasted from another c file.
void sequence_insert_at(Sequence s, int pos, int item)
{
struct node* temp = s->lst;
for(; pos > 0; --pos)
{
temp = temp->rest;
}
add_to_front(&temp, item);
++s->length;
if(!temp->rest)
{
s->end = temp;
}
//s->lst = temp;
}
I don't know why I keep getting a runtime error. if I clone s->lst and traverse the clone, I'm not modifying the pointer to the node in s, but if I change temp, s->lst should have the reflected changes since the nodes are all linked still. Any ideas as to how to fix this? I tried creating another node that is one before the temp after traversal, and then setting it->rest = temp, but that failed as well.
following mistakes a could spot but only so far to get the main function run
new_sequence does not initialize anything in Sequence it creates. lst is not initialized when you access it in sequence_insert_at
struct node* temp = s->lst;
here how it should look like
Sequence new_sequence()
{
Sequence s = malloc(sizeof(struct sequence));
if(!s)
{
printf("Out of memory. Can't allocate s\n");
exit(EXIT_FAILURE);
}
s->lst = malloc(sizeof(struct node));
if(! s->lst) {
printf("Out of memory. Can't allocate lst\n");
}
s->lst->rest = NULL;
s->length = 0;
return s;
}
also s->lst->rest has to be set to NULL, this is what tells that the list has no more elements an not end witch turns obsolete.
struct sequence
{
struct node* lst;
int length;
};
You should be passing the sequence itself to your functions not a pointer to some internal data in the sequence.
add_to_front(&temp, item);
Your sequence_insert_at function should be the one that can handle any position not add_to_front() so it is easier to call with the position 0 from add_to_front() and your having the the hole work done in one function, not a half here and a half there.
void sequence_insert_at(Sequence s, int pos, int item)
{
if(s && pos <= s->length) {
print_sequence(s);
struct node *newnode = malloc(sizeof(struct node));
if (newnode == NULL) {
printf("ERROR! add_to_front ran out of memory!\n");
exit(EXIT_FAILURE);
}
newnode->first = item;
struct node* temp = s->lst;
struct node* prv = NULL;
for(int i = 0; i < pos; i++) {
printf("skip %d\n", temp->first);
prv = temp;
temp = temp->rest;
}
newnode->rest = temp;
if(pos == 0) {
printf("insert as first\n");
s->lst = newnode;
} else {
printf("insert before %d\n", temp->first);
prv->rest = newnode;
}
++s->length;
}
}
and in add_to_front only one statement is needed
void add_to_front(Sequence s, int item) {
sequence_insert_at(s, 0, item);
}
as for inserting at the back of the list
void add_to_back(Sequence s, int item) {
sequence_insert_at(s, s->length, item);
}
A small test with the main function
void print_sequence(Sequence s)
{
struct node* temp = s->lst;
for(int i = 0; i < s->length; temp = temp->rest) {
printf("%d ", temp->first);
i++;
}
printf("\n");
}
int main()
{
Sequence derp = new_sequence();
sequence_insert_at(derp, 0, 14);
add_to_front(derp, 16);
sequence_insert_at(derp, 0, 17);
sequence_insert_at(derp, 2, 15);
add_to_back(derp, 13);
print_sequence(derp);
delete_sequence(derp);
return 0;
}
output is:
17 16 15 14 13
You'll have to go trough the other functions and fix them.
Finally i should note that variable names you have choosen are little bit confusing if not misleading, i would name them this way
typedef struct node {
int data; /* the data that a node holds */
struct node* next; /* the pointer to the next node */
} Node_t;
typedef struct sequence {
struct node* head; /* head or first element of the sequence/list */
int length; /* length is ok but size is better */
} Sequence_t;

Array of pointer to the linked list

I'm working of my assignment, in C, which to store 500 string into strings of 5 char by the mean of hash table with chaining method to fix the collision.
Hashing algorithm : to add up the ASCII value and apply the modulus operator to the result.
The hash table store the hash key generated and a pointer which points to a linked list. Each linked list has more than one element if there are more than one 5-char string that gives the same hash key.
So far this is my code. I compiled it (Codeblock) and it appears that there is no error. However the program crashed.
Please give some inputs on where did i do wrong.
#include <stdio.h>
#include <string.h>
#define SLEN 500
#define WLEN 5
#define MPRIME 73
struct Node {
char s[WLEN+1]; // array to hold the 5-letter word
int sindex; // starting index of the word
struct Node * next; // a pointer to the next word in the list
};
int searchword(char *);
int hashfunc(char *);
void build_hashtbl();
struct Node * hashtable[MPRIME] = {NULL};
char string[SLEN+1] = "thenamewasfamiliartomeonseverallevelslookingbackitwasfatethatifoundhimihadcometopeppervillebeachtocloseonasmallhousethathadbeeninourfamilyforyearsonmywaybacktotheairportistoppedforcoffeetherewasafieldacrossthestreetwherekidsinpurpletshirtswerepitchingandhittingihadtimeiwanderedoverasistoodatthebackstopmyfingercurledinthechainlinkfenceanoldmanmaneuveredalawnmoweroverthegrasshewastannedandwrinkledwithahalfcigarinhismouthheshutthemowerwhenhesawmeandaskedifihadakidoutthereisaidnoheaskedwhatiwasdoing";
int main(void) {
int index;
char query[WLEN+1];
build_hashtbl(); // prepare the hash table
printf("Enter a 5-letter word to search: ");
scanf("%s", query);
index = searchword(query);
if (index != -1)
printf("The word %s starts at index %d.\n", query, index);
else
printf("The word %s is not found.\n", query);
return 0;
}
int searchword(char * word) {
int hashval;
struct Node * lhead;
hashval = hashfunc(word);
lhead = hashtable[hashval];
while (lhead) {
if (strcmp(lhead->s,word) == 0)
return lhead->sindex;
lhead = lhead->next;
}
return -1;
}
int hashfunc(char *){
int hashval = 0;
int i = 0;
for (i = 0; i < WLEN; i++){
hashval += (int) string[i];
}
return (int) (hashval % MPRIME);
}
void build_hashtbl(){
struct Node *hashtable[MPRIME]; //already declared. put here for ease
struct Node * head = NULL;
struct Node * last = NULL;
int i = 0;
int k = 0;
int key = 0;
char sElement[WLEN+1] = {0};
for (i = 0; i <SLEN; i = i+WLEN){ //for every 5 char, find they hashtable index key
key = hashfunc(*string[i]);
for (k = 0; k <WLEN; k++){ //create a new string, sElement from the 5 letter word
sElement[k] = string[i+k];
}
if (hashtable[key] != (NULL)){ //if the hashtable element at that index is empty, STORE it in a node
hashtable[key] = head;
struct Node *new_node;
new_node = (struct Node *) malloc ( sizeof (struct Node) );
strcpy(new_node->s, sElement); //put the new 5 letter word string into the node
new_node->sindex = i; //put the starting index of this word
new_node->next = NULL; //the next pointer is set to NULL
head->next = new_node; //finally set the head node to point to this new node
last = new_node; //set the new node as the last node
}
else { //if there is already a node in the array
struct Node *new_node;
new_node = (struct Node *) malloc ( sizeof (struct Node) );
strcpy(new_node->s, sElement); //put the new 5 letter word string into the node
new_node->sindex = i; //put the starting index of this word
new_node->next = NULL; //the next pointer is set to NULL
head->next = new_node; //finally set the head node to point to this new node
last->next = new_node; //set the last node to point to thew new created node
last = new_node; //set the new node as the last node
}
}
}
Your used last and head uninitialized, so head->next and friends would segfault. In fact you don't need them at all and you don't need your if branches - just replace hashtable[key] by new_node after setting new_node->next to hashtable[key]
void build_hashtbl(){
int i = 0;
int k = 0;
int key = 0;
char sElement[WLEN+1] = {0};
for (i = 0; i <SLEN; i = i+WLEN){ //for every 5 char, find they hashtable index key
key = hashfunc(string+i);
for (k = 0; k <WLEN; k++){ //create a new string, sElement from the 5 letter word
sElement[k] = string[i+k];
}
struct Node *new_node;
new_node = (struct Node *) malloc ( sizeof (struct Node) );
strcpy(new_node->s, sElement); //put the new 5 letter word string into the node
new_node->next=hashtable[key];
new_node->sindex=i;
hashtable[key]=new_node;
}
}
Works for me.
Edit: Also needs #include <stdlib.h> (at least here)

Resources