0

Not sure what is going on with my code. Was implementing a simple binary search tree and got everything to work - had no problem inserting a bunch of elements. Then, while trying to add some file IO functionalities, all of the sudden my program was crashing. I thought perhaps I had messed something up with the file pointers and writing (though that doesn't really make sense either, since it leaves the rest of the code untouched), so I pulled up an archived version of the code, and BAM - crashing after 2 inputs even though it was fully working the last time I tried it!

Adding a bunch of debug print statements (sorry still need to learn to use the debugger), it seems the crash most often occurs at my malloc - but sometimes it randomly crashes at different points if I keep rerunning the program too.

I'm really confused by this. How is it that I was able to insert ~10 elements and now I somehow cant even insert 3? Task manager says I have ~4Gb of RAM free, and it's not like I'm doing some massive amount of inputs - this should cost memory absolutely nothing. Also how is it crashing in different places even though I'm running the exact same code?

I'd be very grateful for any insights. Running Windows 10, Codeblocks as the IDE. Code for the main and the function in question below. In most of my runs, the program crashes before the third insert reaches "Space Allocated", but sometimes it manages to insert it - and then the program crashes anyways, for no apparent reason.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>


typedef struct node *BSTREE;


struct node
{
    int data;
    BSTREE left;
    BSTREE right;
};


BSTREE insert(BSTREE root, int number);
BSTREE find(BSTREE root, int number);
void inOrderTraversal(BSTREE subtree);

int main(){
    BSTREE root = NULL;

    root = (insert(root, 2));
    insert(root, 4);
    insert(root, 1);
}

BSTREE insert(BSTREE root, int number)
{
    printf("\n\nInside insert");

    BSTREE temp = NULL;

    if(!(root)){
    printf("\nInside empty root");


    temp = (BSTREE*)malloc(sizeof(BSTREE));
    printf("\nSpace allocated");


    temp->left = NULL;
    temp->right = NULL;
    printf("\nleft and right set to null");


    temp->data = number;
    printf("\n data set to number");


    root = temp;

    printf("\nroot is now temp; Before returning root");
    printf("\n node data: %d %d %d", root->data, root->left, root->right);

    return root;
    }


    if(number < root->data){
        root->left = (insert(root->left, number));
    }
    else if(number > root->data){
        root->right = (insert(root->right, number));
    }
    else if(number == root->data){
        return root;
    }
}
too honest for this site
  • 12,050
  • 4
  • 30
  • 52
Stazz
  • 11
  • 2
  • Have you got [`valgrind`](http://valgrind.org/) available to you? If so, use it. Also, what you've provided isn't quite an MCVE ([MCVE]). You've not defined the BSTREE type. I suspect you need to read [Is it a good idea to typedef pointers?](http://stackoverflow.com/questions/750178/is-it-a-good-idea-to-typedef-pointers) to which the short answer is "No". – Jonathan Leffler Nov 03 '16 at 03:17
  • For debugging, always _end_ your printing with a newline; the output is not guaranteed to appear until the newline is printed. Newlines at the front may help separate the input from what went before, but the newline at the end is crucial. Absolutely crucial. Also consider the merits of printing diagnostics to `stderr` rather than `stdout`. – Jonathan Leffler Nov 03 '16 at 03:18
  • @JonathanLeffler: Checking out the valgrind - thank you! Don't worry, the BSTREE type was defined - I just left out the define statements and such to avoid clutter (I'll add them back in for clarity though, just to avoid confusion) Also thank you for the tip on the debugging, will fix it. I have no experience using stderr but will look into it. – Stazz Nov 03 '16 at 03:24
  • 1
    In your `main()` function, you have: `root = (insert(root, 2)); insert(root, 4); insert(root, 1);` — I'm curious about the extra set of parentheses since you also use that in the `insert()` function, and you should probably capture the return value from `insert()` each time you use it. With your tree structure and code, it may not matter (except for consistency), but if you start to implement balanced trees, the root node can change as you add more elements to the tree — and then it does matter. – Jonathan Leffler Nov 03 '16 at 03:30
  • **Never ever** `typedef` a pointer! And don't cast the result of `malloc` & friends. – too honest for this site Nov 03 '16 at 04:28

2 Answers2

1

The line:

 temp = (BSTREE*)malloc(sizeof(BSTREE));

is an excellent example of why Is it a good idea to typedef pointers? recommends 'No'.

You have two problems:

  1. You're allocating a pointer to a pointer to a struct node to a pointer to a struct node — you don't need the * in the cast (and there are those who'd argue you don't need to cast the result of malloc()).

  2. You're only allocating enough space for a pointer but you're using it as if it was big enough to hold a struct node; it isn't.

The basic fix is one of these lines:

temp = (BSTREE)malloc(sizeof(struct node));
temp = malloc(sizeof(*temp));

There isn't a way to use BSTREE in the first sizeof operator that I can think of. The second is actually a sound technique; it remains valid even if the type of temp changes. You can create various hybrids too.

I'd recommend using:

typedef struct BSTree BSTree;
struct BSTree
{
    int     data;
    BSTree *left;
    BSTree *right;
};

and then you'd write:

BSTree *temp;

temp = (BSTree *)malloc(sizeof(BSTree));
temp = malloc(sizeof(*temp));

You might note that the second alternative hasn't changed.

Community
  • 1
  • 1
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Crap. Clearly I'm missing some key knowledge here (for context: an assignment in C after not using C for over a year + inadequate teaching of pointers and memory management - and the typedef pointer as part of the skeleton code provided). I figured there was something wrong with using the pointers in the malloc statement, but if I replace the "BSTREE"s with "node" the compiler complains that "node" is undeclared... how do I get it to recognize it as a type? – Stazz Nov 03 '16 at 03:47
  • You could use `typedef struct node node;` — in C++, defining a structure (or class) type is sufficient, but not in C. I merely spelled `node` using `BSTree`. – Jonathan Leffler Nov 03 '16 at 03:49
  • Oh oops just saw the stuff you added on - will try it. – Stazz Nov 03 '16 at 03:49
  • It seems to work (for now haha...) - thank you!!! One last question though since you know what you're talking about - Nad below suggested I free(temp). Now that the program is working, the free statement after "root = temp" consistently throws it into an infinite loop of "Inside Program" - any insight as to why that might be? I'm not doing anything to temp until I recreate it in the recursion, so I'm confused as to why it would cause that. – Stazz Nov 03 '16 at 03:56
  • Assuming you mean "Inside insert" rather than "Inside program", then it is not clear why you get an indefinite loop, but by accessing freed memory, you are invoking _undefined behaviour_ which is unconditionally A Bad Idea™. You don't need to free anything in the `insert()` code. After you've finished creating the tree, you should print it (to check that it has the correct structure — and to ensure you can print it), and you should only then free the tree. You'd do that with `void release(BSTREE node) { if (node != 0) { release(node->left); release(node->right); free(node); } }` or similar. – Jonathan Leffler Nov 03 '16 at 04:18
  • I see - thank you for that clarification. And thanks again for all the help and suggestions - much appreciated! – Stazz Nov 03 '16 at 04:28
0

It seems as you are not returning the memory that you reserve with malloc. When using dynamic memory, it's important to release it again, otherwise you'll have a so called memory leak and the size will just increase until program crashes.

Function for releasing (freeing) memory is free();

A call should look something like free(temp);

I can not try it to make sure because I don't have your library used so I can't guarantee it works, but I hope it solves it.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Nad
  • 11
  • 2
  • @Nad ah it probably does have something to do with that... oops. Adding in free(temp); generates two different outputs - sometimes it crashes right on the free of the first insertion, the other times it results in an infinite loop of "inside insert". Hm, there must be some sort of logic error then - possible I forgot to change something from the archived version... though I could swear I saved the working one... Thank you though, a critical thing I wasn't aware of! – Stazz Nov 03 '16 at 03:35