views:

528

answers:

6

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 (i.e 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;
    }
+1  A: 

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.

danben
I have already tried that : )
iva123
Even the new info (in the edit)?
danben
no still segmentation fault :S root->left is a pointer
iva123
+1  A: 

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.

Bob Jarvis
thanks, alas your suggestion doesn't help for my case :(
iva123
well your code is working but the problem is caused by the main part I think, it still gives segmentation fault
iva123
+1  A: 

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.

Mark Wilkins
+1  A: 

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.

J.J.
thanks but still not working :(
iva123
+10  A: 

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.

nos
yep it solved the problem I removed all * from my code thx : ))
iva123
Good catch, @nos...one way to avoid that problem is to use: `struct decl *d = (struct decl *)malloc(sizeof(*d));`. This gets it right even if the type of `d` changes - and this problem is an example of why it is a good idea.
Jonathan Leffler
+1  A: 

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.

Jonathan Leffler