Search code examples
cgetchar

Evaluate a simple mathematical expression with limited tools, no arrays, and no library functions


Here's a question from the last year's first "Intro to programming" exam at my uni:

Using the getchar() function read an input sequence consisting of numbers, + and - signs. The output should be the result of those arithmetical operations.

For example, if the input is 10+13-12+25-5+100, the output should be 131.

Now, given that I have a little bit of C experience before attending uni, this problem seems easy to solve using pointers, arrays, etc.

But here's the catch: on the exam you can only use things that the students were taught so far. And given that this exam is only like a month after the start of the school year, your options are fairly limited.

You can only use variables, basic input/output stuff, operators (logical and bitwise), conditional statements and loops, functions.

That means no: arrays, strings, pointers, recursion, structures, or basically any other stuff that makes this easy.

How in the hell do I do this? Today is the second time I've spent 3 hours trying to solve this. I have solved it successfully, but only after "cheating" and using arrays, string functions (strtol), and pointers. It's important for me to know how to solve it by the rules, as I'll have similar stuff on the upcoming exam.

Edit: my attempts so far have amounted to using the while loop combined with getchar() for input, after which I just get stuck. I don't have the slightest idea of what I should do without using more "tools".


Solution

  • The solution is quite simple, but it might not be obvious for a beginner. I will not provide a complete program, but rather outline the steps needed to implement this with only a few variables.

    First of all, it's important to notice two things:

    1. Your input can only contain one of -, + or any digit (0123456789).
    2. The getchar() function will read one character of input at a time, and will return EOF when the end of the input is reached or an error occurs.

    Now, onto the solution:

    1. Start by reading one character at a time, in a loop. You will only stop if you reach end of input or if an error occurs:

      int c;
      
      while ((c = getchar()) != EOF) {
          // logic here
      }
      
    2. Start with an accumulator set to 0, and "add" digits to it every time you encounter a digit.

      // outside the loop
      int acc = 0;
      
      // inside the loop
      if (/* c is a digit */)
              acc = acc * 10 + (c = '0');
      

      Hint: that /* c is a digit */ condition might not be simple, you can put this in the else of the check for - and +.

    3. Every time you encounter either - or +, remember the operation, and each time you encounter an operator, first perform the previous operation and reset the accumulator.

      // outside the loop
      int op = 0;
      int result = 0;
      
      // inside the loop
      if (c == '+' || c == '-') {
          if (op) {
              // there already is a previous operation to complete, do it
              if (op == '+')
                  result += acc;
              else
                  result -= acc;
          } else {
              // first operation encountered, don't do anything yet
              result = acc;
          }
      
          acc = 0; // reset
          op = c; // remember the current operation for the future
      }
      
    4. When you reach the end of the input (i.e. you exit the loop), perform the last operation (same logic inside the if from point 3).

    5. Output the result:

      You would normally write something like:

      printf("%d\n", result);
      

      However, if you cannot use string literals ("%d\n") or the printf() function, you will have to do so manually using putchar(). This is basically the opposite of what we did before to scan numbers into an accumulator.

      1. Print the sign first if needed, and make the value positive:

        if (result < 0) {
            putchar('-');
            result = -result;
        }
        
      2. Find the maximum power of 10 that is lower than your number:

        int div = 1;
        
        while (result / div / 10)
            div *= 10;        
        
      3. Use the power to extract and print each digit by division and modulo by 10:

        while (div) {
            putchar('0' + ((result / div) % 10));
            div /= 10;
        }
        

        Note: the '0' + at the beginning is used to convert digits (from 0 to 10) to the relative ASCII character.

      4. End with a newline:

        putchar('\n');