Thread: Binary Tree C Tutorial

  1. #1
    Registered User
    Join Date
    May 2011
    Location
    London
    Posts
    12

    Binary Tree C Tutorial

    Hi there,

    I am new to Binary trees and was studying them in the C tutorial. I am questioning the code posted in that tutorial as an example of inserting. I have marked the lines of code I am questioning with a '-':

    Code:
    insert(int key, struct node **leaf)
    {
        if( *leaf == 0 )
        {
            *leaf = (struct node*) malloc( sizeof( struct node ) );
    -        (*leaf)->left->key_value = key;
            /* initialize the children to null */
    -        (*leaf)->left->left = 0;    
    -        (*leaf)->left->right = 0;  
        }
        else if(key < (*leaf)->key_value)
        {
            insert( key, &(*leaf)->left );
        }
        else if(key > (*leaf)->key_value)
        {
            insert( key, &(*leaf)->right );
        }
    }
    I think accessing the "left" pointer there is actually not initialized at that stage, possibly pointing to some unknown place or even NULL. I think the code should read:

    Code:
    -        (*leaf)->key_value = key;
            /* initialize the children to null */
    -        (*leaf)->left = 0;    
    -        (*leaf)->right = 0;
    The reason is that "*leaf" is the pointer, and the "->" operator is referencing the object pointed to by "*leaf". Which is the newly created struct. The "left" pointer in the struct is not initialized yet, and it is being accessed.

    Like I said, I am new to Binary Trees, so I could be wrong, but knowing pointers to pointers and reading this code, it looks incorrect to me.

    Sincerely,
    Daniel

  2. #2
    Registered User Inanna's Avatar
    Join Date
    May 2011
    Posts
    69
    I think you are right. It looks like a bad translation of the C++ version:
    Code:
    void btree::insert(int key, node *leaf)
    {
      if(key< leaf->key_value)
      {
        if(leaf->left!=NULL)
         insert(key, leaf->left);
        else
        {
          leaf->left=new node;
          leaf->left->key_value=key;
          leaf->left->left=NULL;    //Sets the left child of the child node to null
          leaf->left->right=NULL;   //Sets the right child of the child node to null
        }  
      }
      else if(key>=leaf->key_value)
      {
        if(leaf->right!=NULL)
          insert(key, leaf->right);
        else
        {
          leaf->right=new node;
          leaf->right->key_value=key;
          leaf->right->left=NULL;  //Sets the left child of the child node to null
          leaf->right->right=NULL; //Sets the right child of the child node to null
        }
      }
    }
    This is the way the C version should look:
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct node
    {
        int key;
        struct node* left;
        struct node* right;
    } node;
    
    int insert(node** root, int key);
    void dump(node* root);
    
    int main()
    {
        node* root = NULL;
    
        for (int x = 0; x < 10; ++x)
        {
            if (!insert(&root, rand() % 100))
                break;
        }
    
        dump(root);
    }
    
    int insert(node** root, int key)
    {
        if (!*root)
        {
            *root = (node*)malloc(sizeof(node));
    
            if (!*root)
                return 0;
    
            (*root)->key = key;
            (*root)->left = NULL;
            (*root)->right = NULL;
    
            return 1;
        }
        else if (key < (*root)->key)
        {
            return insert(&(*root)->left, key);
        }
        else
        {
            return insert(&(*root)->right, key);
        }
    }
    
    void dump(node* root)
    {
        if (root)
        {
            putchar('(');
            dump(root->left);
            printf("%d", root->key);
            dump(root->right);
            putchar(')');
        }
    }

  3. #3
    Registered User
    Join Date
    May 2011
    Location
    London
    Posts
    12

    Coded a program

    Actually I just wrote a small program to test the code. My program has one struct allocated in memory, key_value is 5, and I insert a second struct, key_value 7, therefore inserted to the right.

    My code is not art, so please bear with me:

    Code:
    /*
     ============================================================================
     Name        : BTree.c
     Author      : Me
     Version     :
     Copyright   : Your copyright notice
     Description : Hello World in C, Ansi-style
     ============================================================================
     */
    
    #include <stdio.h>
    #include <stdlib.h>
    
    
    //Classic BTree node \ leaf
    struct node{
    	int key_value;
    
    	struct node *left;
    	struct node *right;
    
    };
    
    void insert(int key, struct node **ptr2ptr);
    void destroy_tree( struct node *leaf);
    
    int main(void) {
    	puts("!!!Hello World!!!"); /* prints !!!Hello World!!! */
    
    	struct node *root;
    	struct node **ptr2ptr;
    
    	root = (struct node *)malloc(sizeof( struct node ));
    
    
    	root->key_value = 5;
    	root->left = 0;
    	root->right = 0;
    
    	*ptr2ptr = root;
    
    	insert(7, ptr2ptr);
    
    	destroy_tree (root);
    
    	return EXIT_SUCCESS;
    }
    
    
    void insert(int key, struct node **leaf){
    	if( (*leaf) == 0 ){
    		printf("\nLeaf doesn't exist yet.  Will create leaf with key %d", key);
    		*leaf = (struct node *)malloc(sizeof(struct node));
    		(*leaf)->key_value = key;
    		(*leaf)->left = 0;
    		(*leaf)->right = 0;
    
    	}
    
    	else if( key < (*leaf)->key_value ){
    		printf("\nKey value is smaller.  Will create leaf to the left");
    		insert( key, &(*leaf)->right);
    	}
    	else{
    		printf("\nKey value is greater or equal.  Will create leaf to the right");
    		insert( key, &(*leaf)->left);
    	}
    }
    
    void destroy_tree( struct node *leaf){
    	if(leaf != 0){
    		destroy_tree(leaf->left);
    		destroy_tree(leaf->right);
    		printf("\nAbout to destroy leaf with key %d", leaf->key_value);
    		free(leaf);
    	}
    	else{
    		printf("\nLeaf does not exist.");
    	}
    }

    The output is as follows:
    !!!Hello World!!!

    Key value is greater or equal. Will create leaf to the right
    Leaf doesn't exist yet. Will create leaf with key 7
    Leaf does not exist.
    Leaf does not exist.
    About to destroy leaf with key 7
    Leaf does not exist.
    About to destroy leaf with key 5

    If I make my code where the new struct or node is created the same as in the tutorial:

    Code:
    void insert(int key, struct node **leaf){
    	if( (*leaf) == 0 ){
    		printf("\nLeaf doesn't exist yet.  Will create leaf with key %d", key);
    		*leaf = (struct node *)malloc(sizeof(struct node));
    		(*leaf)->left->key_value = key;
    		(*leaf)->left->left = 0;
    		(*leaf)->left->right = 0;
    
    	}
    ...
    the output is as follows:
    !!!Hello World!!!

    Key value is greater or equal. Will create leaf to the right
    Leaf doesn't exist yet. Will create leaf with key 7
    Leaf does not exist.
    Leaf does not exist.
    About to destroy leaf with key 7
    Leaf does not exist.
    About to destroy leaf with key 5898436
    Leaf does not exist.
    About to destroy leaf with key 5


    As you can see, it is freeing up some memory which it shouldn't be.

    I would recommend that the code in the tutorial for BTree section for the insert function be corrected.

    Best regards,
    Daniel
    Last edited by danielpjdunbar; 05-30-2011 at 08:23 AM.

  4. #4
    Registered User
    Join Date
    May 2010
    Location
    Naypyidaw
    Posts
    1,314
    Code:
    	*ptr2ptr = root;
    Wrong... revise your pointer tutorial.

  5. #5
    Registered User
    Join Date
    May 2011
    Location
    London
    Posts
    12
    @Inanna

    Okay, I see what you mean. Thanks for the reply
    Last edited by danielpjdunbar; 05-30-2011 at 09:08 AM.

  6. #6
    Registered User
    Join Date
    May 2011
    Location
    London
    Posts
    12
    @Bayint Naung

    How is it wrong?
    Last edited by danielpjdunbar; 05-30-2011 at 09:08 AM.

  7. #7
    Registered User Inanna's Avatar
    Join Date
    May 2011
    Posts
    69
    Quote Originally Posted by danielpjdunbar View Post
    @Bayint Naung

    How is it wrong?
    You are dereferencing an uninitialized pointer. ptr2ptr is a reference to root and root is a reference to a node, so it should be
    Code:
    ptr2ptr = &root;
    Since ptr2ptr is superfluous here, you can also do
    Code:
    insert(7, &root);

  8. #8
    Registered User
    Join Date
    May 2011
    Location
    London
    Posts
    12
    @Inanna
    I see what you mean. Yes, I could have done:
    Code:
    insert(7, &root)
    And your code is more easy to read and a clearer syntax.
    Code:
    ptr2ptr = &root;
    I am not sure where I got this syntax from:
    Code:
    *ptr2ptr = root;
    Anyway, the code actually works. I too was confused when I first saw this syntax of setting the value of the pointer pointed to by a pointer.

    I agree your syntax is much clearer, but I also think the dereferencing there is not actually dereferencing the pointer (since it is a pointer to a pointer, not just a pointer), but is actually setting the value of the pointer - which is what is stored in the variable root - i.e. root's address. Confusing ... yes.

    You can see a version of it in this snippet from the insert function:
    Code:
    *leaf = (struct node *)malloc(sizeof(struct node));
    Last edited by danielpjdunbar; 05-30-2011 at 04:42 PM.

  9. #9
    Registered User Inanna's Avatar
    Join Date
    May 2011
    Posts
    69
    I also think the dereferencing there is not actually dereferencing the pointer (since it is a pointer to a pointer, not just a pointer)
    A pointer to a pointer is still a pointer. Look closely at what your code does:
    Code:
    // ptr2ptr points to a random address
    struct node **ptr2ptr;
    
    ...
    
    // ptr2ptr is being dereferenced, but still points to a random address
    *ptr2ptr = root;
    It might work, but that does not mean it will always work. A pointer has to point to a valid address before it can be dereferenced, whether the thing at that address is another pointer or not.

    You can see a version of it in this snippet from the insert function:
    A precondition for insert() is that leaf is a valid pointer, so the dereference is assumed to be safe. In main() you guarantee that ptr2ptr is not a valid pointer and still dereference it.

  10. #10
    Registered User
    Join Date
    May 2011
    Location
    London
    Posts
    12
    @Inanna

    A pointer to a pointer is handled slightly differently than just a pointer. You can see similar code here:
    HowStuffWorks "How C Programming Works"
    and here:
    Chapter 22: Pointers to Pointers

    In the HowStuffWorks page, it is saying that *ptr2ptr is a memory location controlled by the operating system, while ptr2ptr is a pointer controlled by the program.

    I have done a quick program to check the memory addresses, and it appears that just declaring
    Code:
    int **ptr2ptr;
    allocates 2 pointers. The first pointer (or the pointer to the pointer) is already initialized to the address of the other pointer. And that other pointer is initialized to 0. The way you set the value of the second pointer is by:
    Code:
    *ptr2ptr = &x;      // int x;
    By just doing that you have the first pointer, ptr2ptr initialized with the address of *ptr2ptr, and now you have *ptr2ptr with the address of x.

  11. #11
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by danielpjdunbar View Post
    I have done a quick program to check the memory addresses, and it appears that just declaring
    Code:
    int **ptr2ptr;
    allocates 2 pointers. The first pointer (or the pointer to the pointer) is already initialized to the address of the other pointer. And that other pointer is initialized to 0. The way you set the value of the second pointer is by:
    Code:
    *ptr2ptr = &x;      // int x;
    By just doing that you have the first pointer, ptr2ptr initialized with the address of *ptr2ptr, and now you have *ptr2ptr with the address of x.
    I don't know what test you did, but either you misinterpreted it or you're using a slightly-off definition of "allocated". That declaration gives you space for just one pointer. Nothing is initialized to zero (unless you declare it at file scope rather than in a function). The expression "*ptr2ptr" just means "find the variable ptr2ptr, which contains an address, and then go to that address" -- it does not represent a separate piece of memory somewhere.

  12. #12
    Registered User Inanna's Avatar
    Join Date
    May 2011
    Posts
    69
    Quote Originally Posted by danielpjdunbar View Post
    @Inanna

    A pointer to a pointer is handled slightly differently than just a pointer. You can see similar code here:
    HowStuffWorks "How C Programming Works"
    and here:
    Chapter 22: Pointers to Pointers

    In the HowStuffWorks page, it is saying that *ptr2ptr is a memory location controlled by the operating system, while ptr2ptr is a pointer controlled by the program.

    I have done a quick program to check the memory addresses, and it appears that just declaring
    Code:
    int **ptr2ptr;
    allocates 2 pointers. The first pointer (or the pointer to the pointer) is already initialized to the address of the other pointer. And that other pointer is initialized to 0. The way you set the value of the second pointer is by:
    Code:
    *ptr2ptr = &x;      // int x;
    By just doing that you have the first pointer, ptr2ptr initialized with the address of *ptr2ptr, and now you have *ptr2ptr with the address of x.
    Adding a level of indirection does not change the rules. Pointers are very consistent. But you are always free to believe what you want, and I wish you the best of luck.

  13. #13
    Registered User
    Join Date
    May 2011
    Location
    London
    Posts
    12
    Quote Originally Posted by tabstop View Post
    I don't know what test you did, but either you misinterpreted it or you're using a slightly-off definition of "allocated". That declaration gives you space for just one pointer. Nothing is initialized to zero (unless you declare it at file scope rather than in a function). The expression "*ptr2ptr" just means "find the variable ptr2ptr, which contains an address, and then go to that address" -- it does not represent a separate piece of memory somewhere.
    Yes, the declaration does give one variable, ptr2ptr. It also gives it a value, some address. That address is of some other memory location, right - (It is pointing somewhere). That is the other piece of memory (that which variable ptr2ptr is addressing).

  14. #14
    Registered User
    Join Date
    May 2011
    Location
    London
    Posts
    12
    Quote Originally Posted by Inanna View Post
    Adding a level of indirection does not change the rules. Pointers are very consistent. But you are always free to believe what you want, and I wish you the best of luck.
    Yes, I agree with you that pointers are consistent. And I agree with you that *ptr2ptr is dereferencing ptr2ptr. The thing is that ptr2ptr is initialized to something by the operating system by just declaring it a pointer to a pointer.
    Code:
      int **ptr2ptr;
    Try this code:
    Code:
    	int **ptr2ptr;
    
    	printf("\nThe first pointer contains %d",ptr2ptr);
    	printf("\nThe second pointer exists at %d", &(*ptr2ptr));
    	printf("\nThe second pointer contains %d", *ptr2ptr);
    
    	printf("\nAllocating new memory cell");
    	*ptr2ptr = (int *)malloc(sizeof(int *));
    
    	printf("\nThe first pointer contains %d",ptr2ptr);
    	printf("\nThe second pointer exists at %d", &(*ptr2ptr));
    	printf("\nThe second pointer contains %d", *ptr2ptr);
    
            free(*ptr2ptr);
    The output should be:
    ---
    The first pointer contains 2130567168
    The second pointer exists at 2130567168
    The second pointer contains 0
    Allocating new memory cell
    The first pointer contains 2130567168
    The second pointer exists at 2130567168
    The second pointer contains 7278200
    ---

  15. #15
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by danielpjdunbar
    Yes, the declaration does give one variable, ptr2ptr.
    Yes.

    Quote Originally Posted by danielpjdunbar
    It also gives it a value, some address. That address is of some other memory location, right - (It is pointing somewhere). That is the other piece of memory (that which variable ptr2ptr is addressing).
    If the declaration is local to a function, the pointer is not initialised. It does have a value, but that value is likely to be "garbage". In that sense, the pointer does not point to anywhere.

    Quote Originally Posted by danielpjdunbar
    The thing is that ptr2ptr is initialized to something by the operating system by just declaring it a pointer to a pointer.
    That is false, unless ptr2ptr has static storage duration. I could just as easily say that "any variable is initialized to something by just declaring it", and my statement would be equally correct (and equally wrong).

    Quote Originally Posted by danielpjdunbar
    Try this code:
    ... which is rubbish, because you are using ptr2ptr before providing it with an initial value.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Converting From Binary Tree to Threaded Binary Trees
    By elton_fan in forum C Programming
    Replies: 15
    Last Post: 11-08-2007, 11:41 PM
  2. Replies: 2
    Last Post: 08-01-2007, 01:08 PM
  3. Replies: 0
    Last Post: 11-04-2006, 11:07 AM
  4. display tree data in binary tree format
    By xddxogm3 in forum C++ Programming
    Replies: 4
    Last Post: 12-11-2003, 12:47 AM
  5. b-tree (not binary tree) implementation help
    By Unregistered in forum C++ Programming
    Replies: 8
    Last Post: 01-02-2002, 03:30 PM