Book 4: A crude calculator

Previous | Next

Now, what we have done so far was pretty useless, so, when are we going to get to do something that actually is of some value? OK. Let’s try creating our first useful program: a crude calculator.

For this we’ll revamp our main() function again:

#include <stdio.h>
#include <stdbool.h>

int     main()
{
    int     vFirstArg,
            vSecondArg;
    char    vOperation;
    bool    vFinished;
    
    // Make sure our flag is initialized!
    vFinished = false;
    
    // Now loop until user doesn't want anymore:
    while( vFinished != true )
    {
        printf( "What operation do you want to do?\n" );
        scanf( "%c", &vOperation );
        fpurge( stdin );
		
        if( vOperation == '+' )
        {
            printf( "Enter left argument: " );
            scanf( "%d", &vFirstArg );
            fpurge( stdin );
			
            printf( "\nEnter right argument: " );
            scanf( "%d", &vSecondArg );
            fpurge( stdin );
			
            printf( "\n%d + %d = %d\n",
                       vFirstArg,
                       vSecondArg,
                       vFirstArg + vSecondArg );
        }
        else
            vFinished = true;
    }
	
    printf( "Finished.\n" );
    
    return 0;
}

The first new thing here is char, which is the variable type used for a single character. Note that single characters are usually enclosed in single quotes, a.k.a. apostrophes, unlike the usual bunch of text like what we hand to printf() (However, there is also text of 1 character in length, the difference will be explained later). To hand a char to printf() or scanf(), use the %c format sequence.

The second new type is bool, which is short for boolean. A boolean is a value that can only have 2 states, either false or true (think of this as an on/off switch). It is defined in the header stdbool.h that we’re now including in addition to stdio.h.

An example: If you want to write a program that drives a car, you might want to have the driver check whether it’s raining before starting to drive. You’d then use a boolean variable named e.g. isItRaining to remember this, so you can have your commands that hit the brakes when the car needs to stop check isItRaining. If it is raining, you might want to use the more careful braking code so your car doesn’t get out of control.

Anyway, if it rains, you’d assign the value true to it, while on good days you’d set it to false. Variables that contain booleans are often also called flags — think of this as being like US postal mail boxes, which have a little flag on them which can be turned up to indicate to the postman that it contains mail to be picked up.

Now, the first line after the variable declarations assigns the value false to our bool variable vFinished. This is necessary since a newly-declared variable contains garbage. Not zero, not true, not false, just any arbitrary value. If your program wants to use a variable, make sure you have assigned it a value. Assigning a value to a variable the first time is usually also called initializing a variable.

The next line is something new: The While-Loop. A while loop takes the following form:

while( <condition> )
    <command>;

And does the command <command> repeatedly, while the boolean value <condition> is true. That is, it checks whether <condition> is true, if not just goes on with the rest of the program, but if it is, does <command> and checks <condition> again. If it is still true, it just repeats this behaviour endlessly until <condition> becomes false. Note that there is no semicolon behind the braces around the condition.

 

If you have a loop that just seems to get stuck, never doing something, or an if statement that just always seems to think it was true, check whether you accidentally put a semicolon after the condition. A single lone semicolon is considered an “empty command” or a “no-operation”. I.e. you’re asking the computer to do nothing by accident. while and if lines do not end in a semicolon.
So, why is this called a “loop”? Well, if you draw a line from each command to the next as the computer does what the command tells it to, in the case of while, there will actually be a line going back up from the last command inside the loop to the while statement, making a nice small little looped circle.

 

If you’re wondering why we need to do the same commands repeatedly, it’s time for a bit of storytelling again: Every computer program is usually just started, then runs its main() function and quits again. That’s what all our previous programs did: They spilled some text to the terminal and then disappeared again. But more advanced programs can do lots of different things, and even do them in sequence. The art of programming is delaying the end of main() until the user has been able to do what she wanted and signals to us that it’s time for our program to go.

To achieve this, a program usually repeatedly asks the user to enter what she wants to do and then does that. And then asks again, does that, asks again … until the user enters a special thing to do that tells us the user wants to exit our program (also called to quit). When that happens we have to cause our loop to end. For this reason we have vFinished. It’s false when we start our program and stays this way until the user asks us to quit. At that moment, we set it to true to indicate we want to stop repeating our commands and finish.

 

Be careful with your loops. If you don’t write the condition correctly or if you forget to put a line in your script that actually makes the program quit, you will have an endless loop (infinite loop). The only way to get out of an infinite loop that you’re running in Xcode is to click the little “Stop” sign in the upper left next to the “Run” button to shoot down your program.
There are other ways to get out of a loop besides changing variables so the condition is no longer true: There is a command called break that you can use to jump out of a loop immediately, and continue with the commands below the loop. Similarly, you can use a command named continue to jump right back up to the while statement.

 

Trouble is, while expects condition to be true while we want to repeat, whereas vFinished is true when we want to quit. Of course we could just turn vFinished into vKeepRunning and set it to true and be all hunky-dory, but you wanted to learn how to make the computer do what you want, not how to do what the computer wants, right? OK, so how do we fix this?

We introduce the “is not” operator, !=. != compares what’s to its left and right and if both sides have different values (for example, one of them is true and the other is false) it returns true. That is:

1 != 1 (read “one is not one”)
is the same as false, since you can’t find an ounce of truth in the above statement.

2 != 2 (read “two is not two”)
is also the same as false.

1 != 2 (read “one is not two”)
is the same as true, as you will certainly agree that 1 is not 2, just like green is not red and a banana is not your sister (no offence to any bananas reading this).

2 != 1 (read “two is not one”)
is also the same as true, for obvious reasons. Note that you can compare anything using !=, be it booleans, numbers or characters (but not double-quoted text). You can even compare whatever is in a variable to the contents of another variable, but the type must match. I.e. if you are comparing a number to a variable, the variable must be of type int. If you compare true or false to a variable, the variable must be of type bool etc.

So, to get back to our program: The condition of the loop is

vFinished != true

which is true if vFinished is false, and false if vFinished is true. Thus, it is exactly what we need here. Yeah! We won against the computer!

Now an additional problem: I spoke of several commands, but a while loop may only have one command following it. What do we do? Well, there’s also a solution for that: curly brackets! In C, you can use curly brackets to group several commands into one command:

{
    vVariable = 1;
    vVariable = 2;
}

This is one command to C! (But note that they’re still done one after the other by C – you can’t use this to have your computer do five times the work) Thus, if we want several commands in our while loop, we just enclose them in curly braces. Note that even though there is no semicolon after the curly braces, C treats them as if there was a semicolon behind them. I.e.

while( vKeepRunning )
{
    vVariable = 1;
    vVariable = 2;
}

is perfectly valid, just as

while( vKeepRunning )
    vVariable = 1;

You needn’t put a semicolon behind the closing curly bracket (and in most cases, you shouldn’t).

Now, the next two lines in the program are pretty straightforward. We ask the user what she wants to do and then we get the character the user typed using scanf(). The next part is what is interesting. Since we now have whatever the user wanted to do in our char variable vOperation, we now need to take special action depending on whatever is in vOperation. For this, we use the if conditional, which takes the form:

if( <condition> )
    <ifCommand>;
else
    <elseCommand>;

Where it does the command represented by <ifCommand> if <condition> is true, and <elseCommand> when <condition> is false. Note that you can leave away the entire “else” part if you want it to do nothing if <condition> is false, i.e.

if( <condition> )
    <ifCommand>;

Note that while the if-conditional looks similar to the while-loop, an if is only executed once, whereas a while does things repeatedly. if is not a loop.

Now, we use the opposite to the “is not” operator != to compare vOperation to the values we want to handle. The opposite is the == operator, which means “is equal to”. That is, if( vOperation == '+' ) does the <ifCommand> part if vOperation actually contains a “+” character, and else performs the <elseCommand> part. If you’re wondering why the plus sign is enclosed in apostrophes instead of quote characters, re-read my statements on the char type above.

That’s pretty much all that is special about this program. The “if”-part of the “if” conditional simply asks for the two values to add and then outputs the result, using the “+” operator to add the two together. The “else” part simply sets vFinished to true, which causes “while” to stop repeating. When this happens, the program will resume execution after the end of “while”‘s commands and thus write “Finished.” to the screen and exit by returning zero.

 

Keep an eye on your equals signs. In C, = is used for assigning values, == is used for comparing them. It’s easy to mix those up, which can lead to while loops that never stop or never run, or to variables that don’t contain the right value. Since there are some clever tricks that can be done by assigning something in a loop’s condition, the compiler may not always tell you about this error.

Keep in mind that our variables here are of type int, which means they can only hold whole numbers, no fractions. They are also limited to lie roughly between -2 billion and 2 billion. So only type in whole numbers in this range into this calculator, and type only numbers, no thousands separators or commas. Also, if you do the exercise below, you will get slightly low results for numbers that aren’t evenly divisible, because it will strip the fractional part off the results in that case.

 

Now you have a very crude and limited calculator that allows you to perform additions. To end the calculator session, just enter something different than a plus sign.

For those of you who like to experiment, I suggest you try the following: Extend this program to also perform subtraction, multiplication and division. The multiplication operator is * and the division operator is /. Remember to guard against the user dividing a number by zero, as C does not guard against this, and your program will simply crash.

Hint: You can put another if inside an if or else block. Don’t forget to leave in the final “else” statement that assigns true to vFinished, though, or you won’t get out of your program anymore. Below is the finished code you can check against:

int     main()
{
    int     vFirstArg,
            vSecondArg;
    char    vOperation;
    bool    vFinished;
    
    // Make sure our flag is initialized!
    vFinished = false;
    
    // Now loop until user doesn't want anymore:
    while( vFinished != true )
    {
        printf( "What operation do you want to do?\n" );
        scanf( "%c", &vOperation );
        fpurge( stdin );

        if( vOperation == '+' )
        {
            printf( "Enter left argument: " );
            scanf( "%d", &vFirstArg );
            fpurge( stdin );
			
            printf( "\nEnter right argument: " );
            scanf( "%d", &vSecondArg );
            fpurge( stdin );
			
            printf( "\n%d + %d = %d\n",
                       vFirstArg,
                       vSecondArg,
                       vFirstArg + vSecondArg );
        }
        else
        {
            if( vOperation == '-' )
            {
                printf( "Enter left argument: " );
                scanf( "%d", &vFirstArg );
                fpurge( stdin );
                
                printf( "\nEnter right argument: " );
                scanf( "%d", &vSecondArg );
                fpurge( stdin );
         		
                printf( "\n%d - %d = %d\n",
                           vFirstArg,
                           vSecondArg,
                           vFirstArg - vSecondArg );
            }
            else
            {
                if( vOperation == '*' )
                {
                    printf( "Enter left argument: " );
                    scanf( "%d", &vFirstArg );
                    fpurge( stdin );
                        
                    printf( "\nEnter right argument: " );
                    scanf( "%d", &vSecondArg );
                    fpurge( stdin );
                    
                    printf( "\n%d * %d = %d\n",
                               vFirstArg,
                               vSecondArg,
                               vFirstArg * vSecondArg );
                }
                else
                {
                     if( vOperation == '/' )
                     {
                         printf( "Enter left argument: " );
                         scanf( "%d", &vFirstArg );
                         fpurge( stdin );
                        
                         printf( "\nEnter right argument: " );
                         scanf( "%d", &vSecondArg );
                         fpurge( stdin );
                         if( vSecondArg == 0 )
                         {
                              printf( "Flunked maths class, eh?\n" );
                              printf( "You can't divide by zero!\n" );
                         }
                         else
                              printf( "\n%d / %d = %d\n",
                                          vFirstArg,
                                          vSecondArg,
                                          vFirstArg / vSecondArg );
                     }
                     else
                         vFinished = true;
                  }
              }
         }
    }
        
    printf( "Finished.\n" );
        
    return 0;
}

 

There is a second way of writing certain nested conditional statements: The switch statement. I did not introduce switch earlier because it is more limited than if/else. It used to be a way of writing faster code, though these days, compilers are usually good enough that they can make an if just as fast.
It looks like this:

switch( vOperation )
{
    case '/':
        // do something...
        break;

    case '+':
        // do something else...
        break;

    default:
        // do this when vOperation is none of the above.
        break;
}

How a switch statement works is that the computer compares whatever integers you specify in the switch to whatever other integers you specify in the cases. So, it works only on integer values and can only compare for equality. It can’t do != or > or < or >= or <= or any other comparison apart from ==. Also, the numbers after the case label must be constants, that is, you can’t just say case myOtherVariable:, you have to provide an actual number.

If the computer finds a case that matches the switched value, the computer does the commands after that case statement, until it encounters a break statement, or a return statement, or the end of the switch. So, if you have code like:

switch( myIntVariable )
{
    case 1:
        printf( "You are the number one!\n" );
        // FALLING THROUGH! No break here!

    case 2:
        printf( "You are the number two!\n" );
        break;
}

And myIntVariable contains a 1, It will output both “You are the number one!” and “You are the number two!”, because there is no break after the first printf.
However, when myIntVariable contains a 2, only the second printf will be done. If myIntVariable contains any other number, the above code will do nothing. Code like this is hard to read, but there’s one main use for this: You can have several cases that do the exact same commands, e.g.:

switch( myIntVariable )
{
    case 1:
    case 2:
    case 3:
        printf( "Less than four.\n" );
        break;

    case 4:
        printf( "Exactly four\n" );
        break;

    default:
        printf( "Less than one or greater than four.\n" );
}

Feel free to play around with switch statements a little. They are neat, and can make some code easier to read. However, in many cases, you can’t use them, and in others they make the code so unreadable that you’ll prefer using if instead. Also, if you forget a break when writing a switch statement, you can get unexpected behaviour, and it’s hard to find such mistakes. The main reason I mention them here is because you will see code out there that uses them.

There is also a default: label that can be used just like any other case, which indicates where the computer should start doing things if no other case matches the switched number. You can do the same things with default: as we mentioned above. It doesn’t have to be at the end like in these examples, and you can fall through a default: label, or fall through another label after default:, whatever you need.

 

Previous | Next

This entry was posted in C Tutorial. Bookmark the permalink.

13 Responses to Book 4: A crude calculator

  1. James says:

    Nice tutorial

    What software you used to create animated video ?

  2. tamar says:

    Hello,

    Thanks a lot for this great tutorial.

    I’m having trouble understanding what the “trouble” is that you refer to in this paragraph:

    “Trouble is, while expects condition to be true while we want to repeat, whereas vFinished is true when we want to quit. Of course we could just turn vFinished into vKeepRunning and set it to true and be all hunky-dory, but you wanted to learn how to make the computer do what you want, not how to do what the computer wants, right? OK, so how do we fix this?”

    Could you please clarify? Thank you!

    • Uli Kusterer says:

      The “trouble” is that ‘while’ repeatedly performs the commands you write in it while a condition is “true” (hence the name), but our condition is always “false” while we want the ‘while’ to do its thing, and becomes “true” when we want it to stop running. So our situation is the wrong way round to just use ‘while’ without anything else.

  3. Daniel says:

    I’ve put in the code for the first basic calculator (with the +) in, but if I say something like 7+5 when it asks what operation I want to do, it just says “Finished.” Is there something I’m doing wrong or am I entering something wrong? My code looks the same as the one for the + calculator.

  4. Robin says:

    Great tutorial! Thanks getting me excited about C

    I did the exercise and came out just a little different with the divide by zero, but it works also;

    if ( vOperation == ‘/’ )
    {
    printf( “Enter left argument: ” );
    scanf( “%d”, &vLeftArgument );
    fpurge( stdin );

    printf( “Enter right argument: ” );
    scanf( “%d”, &vRightArgument );
    fpurge( stdin );

    // now do the calculation
    if ( vRightArgument != 0 ) {
    printf( “\n%d / %d = %d\n”,
    vLeftArgument,
    vRightArgument,
    vLeftArgument / vRightArgument );
    }
    else
    {
    printf( “Divide by 0 is illegal\n” );
    }

  5. Matt says:

    It looks like you’re missing some {}’s on your ‘else’ statements towards the end of the full calculator code. Specifically at the parts where you’re about to print the division result and right before you set vFinished = true.

    • Uli Kusterer says:

      It looks that way, doesn’t it? 🙂

      In general, it’s a good idea to always put {} around the commands for an ‘if’ or ‘else’ statement, but in this case, although it spans several lines, it is only a single command (there is only a single semicolon at the very end), so this is still correct C code.

  6. Dent says:

    Thank you so much for making this tutorial!
    I advise explicitly stating that you will receive only integers, even if this means you get the wrong number. It may be obvious for some, but it seems like a good idea to be as explicit as possible.

    thank you,

    Dent

  7. Craig says:

    Very worthwhile exercise, thank you!

Leave a Reply

Your email address will not be published. Required fields are marked *


*