0

I am just wondering if it is possible to store the basic arithmetic operators (+, -, *, /) inside variables in C. The reason that I need to do this is because I am trying to build a basic calculator program that accepts up to 5 numbers and can do any of these operations on them. Right now the only way that I have been able to come up with to do this is to store the operators inside a char array and then iterate through it using for loops and switch statements to decide what operator to use for each step. It's pretty clunky and doesn't actually work right now and I am not sure why. Also even if it did work, it wouldn't follow order of operations which I need it to do as well.

This is an example of the output of my program:

Enter a number: 4
Enter an operator (+, -, *, /, or =): +
Enter a number: 8
Enter an operator (+, -, *, /, or =): -
Enter a number: 7
Enter an operator (+, -, *, /, or =): *
Enter a number: 9
Enter an operator (+, -, *, /, or =): /
Enter a number: 2
4.00 + 8.00 - 7.00 * 9.00 / 2.00
0.500000
-6.500000
-6.500000
-3.250000
-3.250000
Result: -3.25
[Finished in 24.13s]

The first 9 lines are just taking the input for the numbers and operators, the 10th line prints out the numbers and operators entered in order, the 11th - 15th lines are supposed to print out the result after each operation which you can see are all incorrect, and the final line prints out the result. What do you think might be causing the math to be incorrect?

Here is my code at the moment:

#include <stdio.h>
#include <stdlib.h>

int main() {
    double nums[5];
    char operators[5];
    double result;
    int i = 0;

    while ((i <= 4) && (operators[i - 1] != '=')) {
        /* Getting the user's input */
        printf("Enter a number: ");
        scanf("%lf", &nums[i]);
        if (i == 4) {
            operators[4] = '=';
        } else {
            printf("Enter an operator (+, -, *, /, or =): ");
            scanf(" %c", &operators[i]);
        }

        i++;
    }
    printf("%.2f %c %.2f %c %.2f %c %.2f %c %.2f\n", nums[0], operators[0], nums[1], operators[1], nums[2], operators[2], nums[3], operators[3], nums[4]);

    for (i = 0; i <= 4; i++) {
        /* Doing the math */
        if (i == 0) {
            /* Getting the value of the first two numbers */
            switch(operators[i]) {
                case '+' :
                    result = nums[0] + nums[1];
                case '-' :
                    result = nums[0] - nums[1];
                case '*' :
                    result = nums[0] * nums[1];
                case '/' :
                    result = nums[0] / nums[1];
            }
            printf("%f\n", result);

        } else {
            /* Iterating through the rest of both arrays and
            continuing to perform operations on the result */
            switch(operators[i]) {
                case '+' :
                    result = result + nums[i + 1];
                case '-' :
                    result = result - nums[i + 1];
                case '*' :
                    result = result * nums[i + 1];
                case '/' :
                    result = result / nums[i + 1];
            }
            printf("%f\n", result);
        }
    }
    // Printing out the answer rounded to 2 decimal points
    printf("Result: %.2f", result);

    return 0;
}

So just to re-iterate my question, is there a way that I could store the operators in a variable as operators instead of chars, which would remove the need for all of the loops, if statements, and switch statements, and if not, what changes should be made to make my code work properly?

P.S. I am only just learning C right now so suggestions for how I should be formatting my code are welcome too.

Shock9616
  • 116
  • 9
  • 4
    If you create functions that perform the operations, then you can store the pointers to the functions in your parse tree. All you have to do is normalize the function parameters to handle binary and unary operations. – Jeff Holt Oct 07 '20 at 20:02
  • Can you give an example of how I might go about that? Like I said, I am still very new to C and am not sure how to do these things yet. – Shock9616 Oct 07 '20 at 20:04
  • The sophisticated solution to this classical problem is to use [lexical analysis with a parser](https://github.com/meyerd/flex-bison-example). – Jeff Holt Oct 07 '20 at 20:09
  • Ok that does pretty much exactly what I need, except that it's way too complicated and I don't really understand anything that's going on in there... – Shock9616 Oct 07 '20 at 20:14
  • Could you explain what's happening? – Shock9616 Oct 07 '20 at 20:15
  • Like Jeff said, use function pointers. Here is an adapted version of your code: https://onlinegdb.com/rk35UsiUw (and here is a similar question: https://stackoverflow.com/questions/252748/how-can-i-use-an-array-of-function-pointers) – Jerry Jeremiah Oct 07 '20 at 20:35
  • The link that Jeff gave is a full parser created by using the parser construction tools Lex and Yacc (the free open source versions are called Flex and Bison) You should look that up because that would help you handle parenthesis and other complicated stuff. – Jerry Jeremiah Oct 07 '20 at 20:40
  • If you can understand what I did with https://www.onlinegdb.com/rk35UsiUw I can make an answer. – Jerry Jeremiah Oct 07 '20 at 20:41
  • Ah ok thank you! Being able to see my own code modified helped a bit. I also watched this video (https://www.youtube.com/watch?v=axngwDJ79GY) just to make sure that I understood what was happening so thank you. – Shock9616 Oct 07 '20 at 20:56

1 Answers1

0

First the bugs:

while ((i <= 4) && (operators[i - 1] != '=')) {

On the first iteration of this loop i is 0 so operators[i - 1] attempts to read before the start of the array. Reading outside of array bounds is undefined behavior.

The incorrect values you're getting is because there's no break statement between each of your switch cases. Without that, the code "falls through" to the next case. So if the operation is subtraction you do that, then multiplication, then division.

Regarding the operators, you can create functions to encapulate each operator, each with the same signature (i.e. return type and number/type of parameters) along with a matching typedef. Then you can have an array of pointers to these functions, and set them to point to the proper function.

Also, you don't need to have a special case for the first set of operators. Just start from index 1 instead of 0 and initialize the result to the first value.

#include <stdio.h>
#include <stdlib.h>

double op_add(double x, double y)
{
    return x + y;
}

double op_sub(double x, double y)
{
    return x - y;
}

double op_mul(double x, double y)
{
    return x * y;
}

double op_div(double x, double y)
{
    return x / y;
}

typedef double (*op_type)(double, double);

int main() {
    double nums[5];
    char operator;
    op_type operators[5];
    double result;
    int i = 0;

    while (i < 5) {
        /* Getting the user's input */
        printf("Enter a number: ");
        scanf("%lf", &nums[i]);
        if (i == 4) {
            operators[i] = NULL;
        } else {
            printf("Enter an operator (+, -, *, /, or =): ");
            scanf(" %c", &operator);
            switch(operator) {
                case '+' :
                    operators[i] = op_add;
                    break;
                case '-' :
                    operators[i] = op_sub;
                    break;
                case '*' :
                    operators[i] = op_mul;
                    break;
                case '/' :
                    operators[i] = op_div;
                    break;
                default :
                    operators[i] = NULL;
                    break;
            }
        }
        if (!operators[i]) break;
        i++;
    }

    result = nums[0];
    for (i = 1; i < 5; i++) {
        if (operators[i-1]) {
            result = operators[i-1](result, nums[i]);
            printf("%f\n", result);
        } else {
            break;
        }
    }
    // Printing out the answer rounded to 2 decimal points
    printf("Result: %.2f", result);

    return 0;
}
dbush
  • 205,898
  • 23
  • 218
  • 273