Implementation of tree with different node types and faux-polymorphism in C
Remarks for beginners:


Never hide a pointer behind a typedef! Never! It makes the code confusing and very hard to read and maintain.
You can't unfortunately use a generic Node pointer like in your code, because this isn't well-defined by the language. There are ways around it, but they are a bit controversial (see advanced reply below).
Generally, when doing any kind of polymorphism, you might want to have an "abstract base class". In this case it would have been NodeType, but it should be a struct of its own and each inherited class should contain an instance of the base class as first member. Not a pointer to it. 
Avoid calling functions in the controlling statement of a for loop like you do in for (size_t i = 0; i &lt; get_num_children(tree); i++). This will often produce needlessly slow code. Instead call the function once before the loop, then store the result in a variable.




Remarks for veterans (beginners ignore):

One advanced trick which is supposedly fully supported by the standard (but might have diverse quality of implementation on different compilers) is the "union common initial sequence" trick.

There's a rule stating that if you expose a union containing all those structs to the same translation unit as where those structs are used, type punning between the structs is well-defined. In your case something like this:

union type_punning
{
  struct VariableNode_ a;
  struct ConstantNode_ b;
  struct OperatorNode_ c;
};


This union just needs to sit there; you don't need to use it, or even show it to the caller. This tells the compiler that since these struct share a common initial sequence, the NodeType member, we can type pun between them and it is well-defined, as long as we only access shared members of that initial sequence.

Meaning that suddenly we can do this:

void func (struct VariableNode_* vn)
{
  if(vn-&gt;type == NTYPE_CONSTANT)
  {
    struct ConstantNode_* cn = (struct ConstantNode_*)vn;
    cn-&gt;const_value = something; // well-defined!
  }
}


There's also exceptions to the strict aliasing rule allowing this use. 

Source: C17 6.5.2.3


  One special guarantee is made in order to simplify the use of unions: if a union contains
  several structures that share a common initial sequence (see below), and if the union
  object currently contains one of these structures, it is permitted to inspect the common
  initial part of any of them anywhere that a declaration of the completed type of the union
  is visible. Two structures share a common initial sequence if corresponding members
  have compatible types (and, for bit-fields, the same widths) for a sequence of one or more
  initial members.