简体   繁体   中英

Traversing a binary tree in C

I'm trying to traverse a binary tree in C. My tree contains an AST node (abstract syntax tree node for compiler). ASTnode reserves nodetype which specifies given node's type (ie INT OP or CHAR and TYPE we don't need to concern other types), the other members are left and right pointers, and finally we store.

Here is code for traversing:

    void traverse(struct ASTNode *root)
    {
        if(root->nodeType == OP){
            printf("OP \n");
            if(root->left != NULL){
              printf("left - ");
              traverse(root->left);
            }
            if(root->right != NULL){
              printf("right - ");
              traverse(root->right);
            }
            return;
        }
        else{
            if(root != NULL && root->nodeType == INT)
            {
              printf("INT - ");
              printf("INT: %d\n",root->value);
            }
            if(root != NULL && root->nodeType == CHAR)
            {
              printf("CHAR - ");
              printf("CHAR: %c\n",root->chValue);
            }
            return;
        }
    }

Also we can't assign left or right values to CONSTANT nodes because in AST, constant values don't contain any extra values.

Updated:

The problem is in my main call:

    int main()
    {
        struct ASTNode *node1 = makeCharNode('a');
        struct ASTNode *node2 = makeCharNode('b');
        struct ASTNode *node10 = makeCharNode('c');
        struct ASTNode *node3 = makeINTNode(19);

        struct decl *d = (struct decl*) malloc(sizeof(struct decl*));
        struct decl *d2 = (struct decl*) malloc(sizeof(struct decl*));

        struct ASTNode *node4 = makeNode(3,d,node3,node2);
        struct ASTNode *node5 = makeNode(3,d2,node4,node1); !!
        traverse(node4);
    }

If we delete node5 (which is marked by !!) the code works very well otherwise it gives a segmentation fault.

Functions that operate on makenode :

    struct ASTNode *makeNode(int opType,struct decl *resultType,struct ASTNode *left,struct ASTNode *right)
    {
        struct ASTNode *node= (struct ASTNode *) malloc(sizeof(struct ASTNode *));
        node->nodeType = opType;
        node->resultType = resultType;
        node->left = left;
        node->right = right;
        return node;
    }

    struct ASTNode *makeINTNode(int value)
    {
        struct ASTNode *intnode= (struct ASTNode *) malloc(sizeof(struct ASTNode *));
        intnode->nodeType = INT;
        intnode->value = value;
        return intnode;
    }

    struct ASTNode *makeCharNode(char chValue)
    {
        struct ASTNode *charNode = (struct ASTNode *) malloc(sizeof(struct ASTNode *));
        charNode->nodeType = CHAR;
        charNode->chValue = chValue;
        return charNode;
    }

Your mallocs are wrong

 struct decl *d = (struct decl*) malloc(sizeof(struct decl*));
 struct decl *d2 = (struct decl*) malloc(sizeof(struct decl*));

need to be

 struct decl *d = (struct decl*) malloc(sizeof(struct decl));
 struct decl *d2 = (struct decl*) malloc(sizeof(struct decl));

(or use sizeof *d instead of sizeof(struct decl)). In C you don't need to cast the return value of malloc , btw.

Also, make sure you're setting members to NULL or another default value before you access them. malloc will not set them to 0/NULL for you.

All I can see is that maybe you'd want to check root->value et al before passing to printf.

Also, though this shouldn't cause a bug, you might want to change

if(root != NULL && root->nodeType == CHAR)

to

else if(root != NULL && root->nodeType == CHAR)

Edit: wait, here's something - when you pass root->left to traverse, is that the value itself or a pointer? The function expects a pointer.

You're not checking to see if 'root' is NULL in your first 'if' block. I think the easiest thing to do is to wrap

if(root != NULL)

around the whole thing (except the final return), and get rid of the separate 'root != NULL' checks when printing the INT and CHAR nodes.

Share and enjoy.

EDIT: here's what I mean:

void traverse(struct ASTNode *root) 
  { 
  if(root != NULL)
    {
    switch(root->nodeType)
      {
      case OP:
        printf("OP \n"); 

        if(root->left != NULL)
          { 
          printf("left - "); 
          traverse(root->left); 
          } 

        if(root->right != NULL)
          { 
          printf("right - "); 
          traverse(root->right); 
          } 
        break;

      case INT:
        printf("INT - "); 
        printf("INT: %d\n",root->value);
        break;

      case CHAR:
        printf("CHAR - "); 
        printf("CHAR: %c\n",root->chValue); 
      }
    } 
  }

Also changed to use a switch instead of a bunch of if's.

Please excuse any syntax errors: this is off the top of my head with no compiler handy.

The makeNode function needs to initialize all members of the structure. Is it perhaps not setting one of the left/right pointers to NULL? The malloc() call does not zero the memory.

Ack - I'm blind. The answer by nos is the correct one. Incorrect malloc call. I was just adding to the noise.

Your code will segfault at the first NULL node passed into traverse().

You take care to check root != NULL within your else block, but by then you have already dereferenced it. If you try to dereference a NULL pointer, you'll segfault.

Try adding

if (!root) return;  

as your first line.

You show code allocating ' struct decl ' pointers; you don't show the code initializing them. It is not clear why makeNode() would initialize its second argument, or how it would do so. It is also not clear what the 3 means. Nor is it clear how the ' struct decl ' integrates into the ASTnode.

You can simplify traverse() by dealing with exceptional cases early:

void traverse(struct ASTNode *root)
{
  if (root == NULL)  // Or: assert(root != NULL);
    return;
  if (root->nodeType == OP)
  {
    printf("OP \n");
    if (root->left != NULL)
    {
      printf("left - ");
      traverse(root->left);
    }
    if (root->right != NULL)
    {
      printf("right - ");
      traverse(root->right);
    }
  }
  else if (root->nodeType == INT)
  {
    printf("INT - ");
    printf("INT: %d\n", root->value);    
  }
  else if (root->nodeType == CHAR)
  {   
    printf("CHAR - ");
    printf("CHAR: %c\n", root->chValue);
  }
  else
    assert("Unrecognized nodeType" == 0);
}

This also deals with the impossible case - an unrecognized node type. It also does not crash and burn if a null pointer is passed in.

However, this does not yet explain why your code crashes and doesn't crash depending on the extra ASTnode...I'm not convinced we have enough code to be sure of why that happens. Have you tried traversing all the nodes (node1, node2, node3, node10)? These should be fine assuming the makeCharNode() and makeINTNode() functions work sanely, but such rudimentary checks can save your bacon. It might be helpful to see what the makeNode() function does; does it ensure all parts of the node are properly initialized.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM