codeplea / tinyexpr

tiny recursive descent expression parser, compiler, and evaluation engine for math expressions
https://codeplea.com/tinyexpr
zlib License
1.61k stars 245 forks source link

Weird behavior when calling te_free #90

Closed Sercurio closed 2 years ago

Sercurio commented 2 years ago
#include <stdio.h>
#include "../docs/lib/TinyExpr/tinyexpr.h"

int main()
{
  double t = 0;
  te_expr *expr;
  int err;

  te_variable vars[] = {{"t", &t}};
  expr = te_compile("t", vars, 1, &err);
  te_free(expr);
  if (expr)
  {
    printf("if\n");
    printf("%f\n", te_eval(expr));
  }
  else
    printf("else");
}

This simple program calls te_free before testing expr in an if condition.

The weird behavior is that when I launch several times the program, sometimes I get a Segmentation fault and sometimes not.

Terminal output:

[louis@PORT-8578 test]$ ./a.out 
if
0.000000
[louis@PORT-8578 test]$ ./a.out
if
0.000000
[louis@PORT-8578 test]$ ./a.out
if
Segmentation fault
codeplea commented 2 years ago

You should only call te_free() when you're done with it. You can't call te_eval() after calling te_free() or a seg-fault is the expected outcome.

Sercurio commented 2 years ago

In my program, I need to evaluate a formula based on user input inside a loop. Should I call te_free() before exiting the program or before compile a new formula ?

codeplea commented 2 years ago

Call free when you're done with it. Don't call it before that.

Here's how you would do it in the simple example you gave:

#include <stdio.h>
#include "../docs/lib/TinyExpr/tinyexpr.h"

int main()
{
  double t = 0;
  te_expr *expr;
  int err;

  te_variable vars[] = {{"t", &t}};
  expr = te_compile("t", vars, 1, &err);
  if (expr)
  {
    printf("if\n");
    printf("%f\n", te_eval(expr)); //<-- This is the last place expr is used!!!!
    te_free(expr); //<--- Free after last use.  After te_free returns, can't use expr anymore.
}
  else
    printf("else");
}

I recommend you read up on memory management in C. It's very common to allocate memory with e.g. malloc() and then you must clean up that memory using free() - but only when you're done using it. Many, many libraries, tinyexpr included, use the same idiom.

The call to te_compile allocates resources. You then use those resources with other functions, such as te_eval(). Only when you're completely done with it, you call te_free() to release the allocated resources.