Most expression trees use pointers because recursive data structures are impossible by-value. E.g. something like this won't work:
struct Expr {
Expr left;
Expr right;
...
}
Additionally, expression trees usually contain multiple different expression types. There might be nodes for
- terminal expressions like literals or variables,
- unary expressions like field access or negation
- binary expressions like addition, multiplication, or
- ternary expressions like if-then-else conditionals.
In OOP languages, that is usually modelled with a class hierarchy with an Expr
interface on top and various subclasses. However, subtype polymorphism requires pointer indirection. Since the size of a subtype isn't known, you can't assign a subtype instance by value to storage allocated for a base class. Some languages may hide this to some degree.
In your example, you are already using a pointer to get around this problem: the Expr[]
array. Since the array storage is separate from the array variable, this circumvents the recursive-size problem. As you (quite unusually) only have a single Expr
type, no subtyping problems arise.
(Are you by chance modelling S-expressions? Even they generally have atoms for literals and symbols.)
Using pointers is attractive if your expression nodes are also immutable. Any AST transformations (like constant folding) will replace some nodes but reuse other nodes unchanged. If they can just be hooked into the new AST by pointer, that is much cheaper than duplicating the whole AST-subtree.