How to free the call stack in C?

155 Views Asked by At

I have created a calculator using RDP (recursive descent parser) to parse and evaluate mathematical expressions "such as: 5cos(30) -5(3+5)". The thing is I also have tried to include commands like "config" which lets the user configure the angle units (Gra, Rad, Deg). And here I found a problem with the design of the code.

Simplified example of the call stack:

int main()
{
   parseexpr()
    {
       parseterm()
       {
          parsefactor()
          {
              parsecommand() or parsefct() or parsenumber();
          }

       }
    }
}

Now when the user inputs the "config" command. I would want to let him configure the calculator (the configuration is inside a .txt file) and just jump back to "main()" (where he would be asked to enter expression again) since it would be problematic to return through the call stack, as I would need a return value, which "parsecommand()" cannot return.

My question is how can I clean the call stack such that I would return to main directly through a "goto" statement ?

1

There are 1 best solutions below

18
Lundin On

My question is how can I clean the call stack such that I would return to main directly through a "goto" statement ?

Don't do that. That's actually a so-called "XY question".

The correct way to go about it is this:

  • Place all these functions in a module of their own, lets call it parse.h / parse.c.

  • In parse.h come up with a result/error type to be used by all applicable functions in this module. Something like:

    typedef enum
    {
      /* whatever results that may make sense: */
      PARSE_OK,
      PARSE_BAD_FORMAT,
      PARSE_DIV_BY_ZERO,
      ...
    } parse_result_t;
    
  • Applicable functions are then defined as:

    parse_result_t parse_expr (/* parameters */);
    

    Where any values that need to be returned are passed as pointer parameters.

  • In a nested number of function calls, each function will check the result and if not OK, return an error code to the caller. Often it is considered good practice to not modify the value parameters upon error.


The incorrect, very bad practice alternative is to utilize setjmp/longjmp which are low level functions actually storing and restoring the callstack and other similar variables. So it's theoretically possible to do in standard C, but these functions are bad practice because:

  • In a properly designed program, each function has no clue of who called it. It only knows which functions it will call itself. A properly designed program is a tree dependency structure with main() at the very top.
  • Providing information of why something failed through a result variable enables various error checking/handling rather than just "oops something went wrong".
  • By using return values rather than a global state variable, the functions are thread-safe and self-contained.
  • By making the functions self-contained, there is no "tight coupling" to unrelated functions or variables. For example if I pick up your parse library, I don't want some strange linker error referring to some jmp_buf global variable being missing, which I'm then somehow supposed to implement in main(). Or worse yet: in order to link parse.c I must also link main.c. Such tight coupling dependencies are neither portable nor maintainable, it's bad in almost every imaginable way.
  • Non-conditionally jumping to unrelated parts of the program has been considered bad programming since the 1960s ("GoTo Considered Harmful") and is referred to as "spaghetti programming" - the analogy being that your program will start to resemble a plate of spaghetti, where one strand might end up elsewhere at some unexpected, unrelated location. Incredibly difficult to read and maintain.
  • setjmp/longjmp come with a long list of hazards and potential undefined behavior.