-1

I'm trying to build a binary search tree. Inserting an integer using insert function (only using 1 to 100 for testing) and appending the result to a file using inorder traversal. However, i'm getting a segmentation fault error. Using Visual Studio code on Macbook Pro 2020. Also tested on Codeblocks on Windows - filename.exe stops working and crashes.

#include <stdio.h>
#include <stdlib.h>
    
typedef struct node *BST;
    
struct node {
    int data;
    BST left;
    BST right;
};

BST insert(BST root, int number) {
    BST temp = NULL;
    if (root == NULL) {
        temp = *(BST *)malloc(sizeof(BST));
        temp->left = NULL;
        temp->right = NULL;
        temp->data = number;
        root = temp;
        }
    else if (root->data > number) {
        insert(root->left, number);
    }
    else {
        insert(root->right, number);
    }
    return root;
}

//returning null if number not found and pointer to root if found
BST find(BST root, int number) {
    if (root == NULL) {
        return NULL;
    }
    
    if (root->data > number) {
        find(root->left, number);
    }
    else if (root->data < number) {
        find(root->right, number);
    }
    else (root->data = number);
        return root;
}
    
//printing number to file
void inOrder (BST root, FILE *fp) {
    if (root == NULL) return;
    inOrder(root->left, fp);
    fprintf(fp, "%d", root->data);
    inOrder(root->right, fp);
}

int main() {
    BST root = NULL;
    int n = 100;
    int treeArray[n];
    for (int r = 0; r < n; r++) {
        treeArray[r] = r;
    }
    root = insert(root, treeArray[0]);
    for (int x = 1; x < n; x++) {
        insert(root, treeArray[x]);
    }
    
    FILE *treefile = fopen("print_tree.txt", "w");
    inOrder(root, treefile);
    fclose(treefile);
    return 0;
}
    
Error: /bin/sh: line 1: 44278 Segmentation fault: 11 *file path redacted*

What am I doing wrong here? :(

userx
  • 11
  • 4
  • So what did you find out in the debugger? At a minimum it tells you exactly which line of code triggered the seg fault and then you can examine the variables to see what sticks out. Learning to debug yourself effectively is going to be much more valuable to you than getting someone else to debug for you. – kaylum Jul 30 '20 at 06:09
  • 2
    `temp = *(BST *)malloc(sizeof(BST));` should be `temp = malloc(sizeof(*BST));`. Suggest not using `typedef` for pointers as it causes confusion. – kaylum Jul 30 '20 at 06:25
  • Genreral hint: https://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc – Hulk Jul 30 '20 at 06:29
  • 1
    Just one tip, in-general segmentation fault, occurs in C related to memory related issues, in such cases always check your allocating enough memory or not accessing something beyond allocated memory. – danglingpointer Jul 30 '20 at 06:39
  • 1
    @danglingpointer Or... cast & reference a pointer to improper type – makerj Jul 30 '20 at 06:45
  • 2
    `temp = *(BST *)malloc(sizeof(BST));` is wrong. Use the form: `temp = malloc(sizeof *temp);` Then you'll always get the size correct. – Support Ukraine Jul 30 '20 at 07:07
  • Somebody please make an answer. I like e.g. the comments by kaylum and @4386427 well enough to accept as useful answer. Additional explanation appreciated but optional. – Yunnosch Jul 30 '20 at 07:27
  • @Yunnosch I haven't looked at all the code so I'm not sure that this is the root cause. Anyway - remember that you are free to write an answer using stuff from others comments - so if you have the time, feel free to use whatever you want from my comment. I don't have the time now. – Support Ukraine Jul 30 '20 at 07:31
  • @4386427 Ah, fair enough. It seemed so confident (no complaint) and convincing. @ userx Hi, if/when you verified the helpfulness of the comments above, you are free to use them (at least the one by 4386427) to make your own answer. I mention because I prefer Q/A pairs to Q/comment pairs. – Yunnosch Jul 30 '20 at 07:34
  • 1
    @SaiSreenivas Yeah it's a typo on my part. Should be `sizeof(*temp))` – kaylum Jul 30 '20 at 08:10

2 Answers2

1

You declare BST as:

typedef struct node *BST;

So, it is a pointer to a struct node and your problem is probably here:

temp = *(BST *)malloc(sizeof(BST));
  1. You are allocating memory but the byte size you specified is that of BST, that is pointer to struct node, while you want to allocate a struct node.

  2. You cast the returned value of malloc to a pointer to a BST, that is, a pointer to a pointer to a struct node. And then you de-reference it to assign the result to temp. So, what you assign to temp is a pointer to a struct node (correct) but the pointed object has the wrong size. Anyway, you should not cast the value returned by malloc (void *).

Things are much simpler than you apparently think: if you want to allocate a struct node (or anything else) pass its size to malloc. malloc returns a void * pointer to the allocated struct node that you can assign to any pointer variable without casting.

Note: you should check the returned value because if malloc fails it returns NULL and you should not use that.

Try this, instead:

temp = malloc(sizeof (struct node));
if(temp == NULL) {
  fprintf(stderr, "%s:%d allocation failed\n", __FILE__, __LINE__);
  exit(EXIT_FAILURE);
}

I assumed that an allocation error, in your case, is unrecoverable, adapt if it is.

Note: you could also, as suggested in comments, use:

temp = malloc(sizeof *temp);

which will always work, by construction. But as long as you are not completely comfortable with pointers and memory allocation I suggest that you use explicit types with malloc. It is a bit easier to read and understand, even if it is a bit less easy to maintain (if you change the type of temp).

But there are other problems with your code. Your insert and find functions are bogus. Understanding why is left as a debugging exercise.

Renaud Pacalet
  • 25,260
  • 3
  • 34
  • 51
1

Main problem is with this statement:

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

As it was already explained in the other answer clearly, I am going to skip that.

So you can do:

temp = malloc(sizeof (struct node));

OR

// to add : this helps in case the type of temp ever changes,
temp = malloc(sizeof(*temp));

Other minor logical changes:

  • In the find function, there is no need of this else if statement
    // = or == doesn't matter now
    else (root->data = number);
  • In the insert function, you forgot to link the nodes which you insert after your root node. So whenever you perform the inorder traversal, you only get the root node i.e 0 in your case.

Note:

typedef is used to make code more readable, but hiding the pointer using typedef creates confusion. So I would suggest not to typedef pointers.

Sai Sreenivas
  • 1,690
  • 1
  • 7
  • 16
  • 1
    Nitpick: The second form is not only better in case the type of temp changes. It's also (and IMO more important) better because it locks the sizeof to whatever the pointer points to. Therefore it's less error prone - you just can't get it wrong. Anyway +1. – Support Ukraine Jul 30 '20 at 09:51
  • Correct. I used temp = malloc(sizeof (struct node)); and this helped. I also made some more tweaks to the insert function, removed recursion and the code is much more efficient now. Thanks for the help everyone! – userx Jul 30 '20 at 14:49