Ultimate Tc Ultimate Tc - 1 month ago 6
C Question

Segmentation Fault (Core Dumped) when implementing binary tree

My code is having segmentation fault (core dumped) here. I'm not pretty sure which line is causing it since I'm pretty new to C.
What I am trying to do here is to implement a binary search tree for each line on the file (only for insertion and search). Each line will not be more than 1000 words.

Here's my code:

BST *bst_ins(BST *bst, char *key, void *value)
{
BST* temp = NULL;
int cmp = strcmp(bst->kvp.key, key);
if(bst == NULL)
{ /* This for null node */
temp = (BST*)malloc(sizeof(BST)); /* allocate the memory of struct for new node */
temp->left = NULL;
temp->right = NULL;
temp->kvp.key = key;
temp->kvp.value = value;
}


else if(cmp < 0) /* Current node less than input */
{
bst->right = bst_ins(bst->right,key,value);
}

else if(cmp > 0)
{
bst->left = bst_ins(bst->left,key,value);
}

return bst;
}


KVP *bst_get(BST *bst, char *key)
{
KVP* return_this;
if(bst!=NULL)
{
if(bst->kvp.key==key)
{
return return_this;
}
else if(strcmp(bst->kvp.key, key) < 0) /* Current node less than input */
{
return bst_get(bst->left, key);
}
else if(strcmp(bst->kvp.key,key) > 0)
{
return bst_get(bst->right,key);
}
}
return 0;
}


Below is header.h

typedef struct {
char *key;
void *value;
} KVP;
typedef struct bst {
struct bst *left;
struct bst *right;
KVP kvp;
} BST;


Could someone please help me figure out which line is causing it?
Thanks.

EDIT:

SOLVED! FINALLY :D

Here's the main function.

int main()
{
char* str1=strdup("alokama");
char* str2=strdup("kokokoko");
BST *bst = NULL;
bst = bst_insert(bst,str1,NULL);
bst = bst_insert(bst,str2,NULL);
if(bst_get(bst,str1)){ printf ("yuhuu\n"); }
return 0;
}

Answer

When you call bst_ins, bst may be NULL, so you can't dereference it. Also, you define a temporary node, which is NULL. When you get to your string comparisons,

if (strcmp(temp->kvp.key, key) < 0) ...

you certainly dereference a NULL pointer. Not good. The code below fixes the issue and also calls strcmp only once per pass. Note how temp is defined only in the scope where a new node is created.

BST *bst_ins(BST * bst, char *key, void *value)
{
    int cmp;

    if (bst == NULL) {
        BST *temp = (BST *) malloc(sizeof(*temp));

        temp->left = NULL;
        temp->right = NULL;
        temp->kvp.key = key;
        temp->kvp.value = value;

        return temp;
    }

    cmp = strcmp(bst->kvp.key, key);

    if (cmp < 0) {
        bst->right = bst_ins(bst->right, key, value);
    } else if (cmp > 0) {
        bst->left = bst_ins(bst->left, key, value);
    }

    return bst;
}

You can now insert new nodes like so:

bst = bst_ins(bst, "plum", "1");
bst = bst_ins(bst, "apple", "2");
bst = bst_ins(bst, "orange", "3");
bst = bst_ins(bst, "kumquat", "4");

But that is a bit clumsy, because you have to assign the result to the root node, which is redundant and easy to forget. You also lose the useful possibility to return the (possibly new) node associated with the key.

A better approach might be to pass the address of the root node to functions that may change the tree. If you are not afraid of the (*bst) notation and the address-of poerator &, here goes:

BST *bst_ins(BST **bst, char *key, void *value)
{
    int cmp;

    if (*bst == NULL) {
        *bst = (BST *) malloc(sizeof(**bst));

        (*bst)->left = NULL;
        (*bst)->right = NULL;
        (*bst)->kvp.key = key;
        (*bst)->kvp.value = value;

        return *bst;
    }

    cmp = strcmp((*bst)->kvp.key, key);

    if (cmp < 0)  return bst_ins(&(*bst)->right, key, value);    
    if (cmp > 0)  return bst_ins(&(*bst)->left, key, value);

    return *bst;
}

and call it like so:

bst_ins(&bst, "plum", "1");
bst_ins(&bst, "apple", "2");
bst_ins(&bst, "orange", "3");
bst_ins(&bst, "kumquat", "4");

The function returns the node associated with the key, but you can easily ignore the return value.

Finally, make sure that your tree is properly deleted when you are done.