3. Variables, Expressions, and Statements (Oh My)


"It takes all kinds to make a world, does it not, Padre?"
"So it does, my son, so it does."
Pirate Captain Thomas Bartholomew Red to the Padre, Pirates

There sure can be lotsa stuff in a C program.

Yup.

And for various reasons, it'll be easier for all of us if we classify some of the types of things you can find in a program, so we can be clear what we're talking about.

3.1. Variables

A variable is simply a name for a number. The number associated with that variable is said to be it's value. You can change the value later, too. One minute a variable named foo might have the value 2, the next you might change it to 3. It's variable, see?

Variables can have different types, as well. In C, because it's such a picky language about types (I'm saying that emphatically to make strongly-typed language fanatics roll in their future graves) you have to know in advance what type of numbers you'll be storing in the variable.

Before you can use a variable in your code, you have to declare it. This way the compiler knows in advance as it runs down your program how to treat any variables. Here is an example that shows a number of different types, and their corresponding sets of numbers they represent:

int main(void)
{
    int i;    /* holds signed integers, e.g. -3, -2, 0, 1, 10 */
    float f;  /* holds signed floating point numbers, e.g. -3.1416 */

    printf("Hello, World!\n"); /* ah, blessed familiarity */

    return 0;
}

In the above example, we've declared a couple of variables. We haven't used them yet, and they're both uninitialized. One holds an integer number (random, to start, since we never initialized it), and the other holds a floating point number (a real number, basically.)

What's this? You want to store some numbers in those variables? Well, ask your mother; if it's all right with her, it's all right with me.

So, how to do that...you use the assignment operator. An operator is generally a piece of punctuation that operates on two expressions to produce a result. There are a number of them, like the addition operator (+) and the subtraction operator (-), but I'm getting ahead of myself. We'll talk about those more in the expressions section, below. In this case, we're going to use the assignment operator, which is =, to assign a value into a variable:

int main(void)
{
    int i;

    i = 2; /* assign the value 2 into the variable i */

    printf("Hello, World!\n");

    return 0;
}

Killer. We've stored a value. But don't run off an implement a clone of Quake III just yet; let's try to do something with that variable, just so we can say we did. Let's print it out using printf(), for instance.

We're going to do that by passing two parameters to the printf() function. The first argument is a string that describes what to print and how to print it (called the format string), and the second is the value to print, namely whatever is in the variable i.

printf() hunts through the format strings for a variety of special sequences which start with a percent sign (%) that tell it what to print. For example, if it finds a %d, it looks to the next parameter that was passed, and prints it out as an integer. If it finds a %f, it prints the value out as a float.

As such, we can print out the value of i like so:

int main(void)
{
    int i;

    i = 2; /* assign the value 2 into the variable i */

    printf("Hello, World!  The value of i is %d, okay?\n", i);

    return 0;
}

And the output will be:

Hello, World!  The value of i is 2, okay?

And now, on to expressions! We'll do all kinds of fancy stuff with these variables.

3.2. Operators

I've snuck a few quick ones past you already when it comes to expressions. You've already seen things like:

result += i;
i = 2;

Those are both expressions. In fact, you'll come to find that most everything in C is an expression of one form or another. But here for the start I'll just tell you about a few common types of operators that you probably want to know about.

i = i + 3;  /* addition (+) and assignment (=) operators */
i = i - 8;  /* subtraction, subtract 8 from i */
i = i / 2;  /* division */
i = i * 9;  /* multiplication */
i++;        /* add one to i ("post-increment"; more later) */
++i;        /* add one to i ("pre-increment") */
i--;        /* subtract one from i ("post-decrement") */

Looks pretty weird, that i = i + 3 expression, huh. I mean, it makes no sense algebraically, right? That's true, but it's because it's not really algebra. That's not an equivalency statement--it's an assignment. Basically it's saying whatever variable is on the left hand side of the assignment (=) is going to be assigned the value of the expression on the right. So it's all ok.

An expression? What's that? Sorry, but I'm far to lazy to cover it here, so I'll cover it in the next section.

3.3. Expressions

This is actually one of the most important sections in the guide about C++. So pay attention and be seated, you naughty children!

Now that everything's in order, we'll...actually, let's do the important part a little later. Now we'll talk about what those expression things are a little bit.

An expression in C consists of other expressions optionally put together with operators. I know that's a self-referential definition, but those are always good for a laugh, aren't they? (Pick up any old C book and look up recursion in the index and see what pages it leads you to.)

The basic building block expressions that you put together with operators are variables, constant numbers (like 10 or 12.34), and functions. (We'll talk about functions later, although if you recall we've already had a run-in with the printf() function.)

And so when you chain these together with operators, the result is an expression, as well. All of the following are valid C expressions:

i = 3
i++
i = i + 12
i + 12
2 
f += 3.14

Now where can you use these expressions? Well, you can use them in a function call (I know, I know--I'll get to function real soon now), or as the right hand side of an assignment. You have to be more careful with the left side of an assignment; you can only use certain things there, but for now suffice it to say it must be a single variable on the left side of the assignment, and not a complicated expression:

radius = circumference / (2.0 * 3.14159);        /* valid */
diameter / 2 = circumference / (2.0 * 3.14159);  /* INVALID */

(I also slipped more operator stuff in there--the parentheses. These cause part of an expression (yes, any old expression) to be evaluated first by the compiler, before more of the expression is computed. This is just like parentheses work in algebra.)

Well, I was going to talk about that important thing in this section, but now it looks like we're running behind a bit, so let's postpone it until later. I promise to catch up, don't you fear!

3.4. Statements

For the most part, you're free to make up variable names to your heart's content, calling them whatever you'd like. There are no exceptions, except for statements and other reserved words which you may not use unless you use them in the officially (by the compiler) prescribed manner.

That definition was rather glib. I should rewrite it. Or maybe we'll just sojourn bravely on! Yes!

What are these pesky statements? Let's say, completely hypothetically, you want to do something more than the already amazingly grand example program of assigning a value to a variable and printing it. What if you only want to print it if the number is less than 10? What if you want to print all numbers between it and 10? What if you want to only print the number on a Thursday? All these incredible things and more are available to you through the magic of various statements.

3.4.1. The if statement

The easiest one to wrap your head around is the conditional statement, if. It can be used to do (or not do) something based on a condition.

Like what kind of condition? Well, like is a number greater than 10?

int i = 10;

if (i > 10) {
    printf("Yes, i is greater than 10.\n");
    printf("And this will also print if i is greater than 10.\n");
}

if (i <= 10) print ("i is less than or equal to 10.\n");

In the example code, the message will print if i is greater than 10, otherwise execution continues to the next line. Notice the squirrley braces after the if statement; if the condition is true, either the first statement or expression right after the if will be executed, or else the collection of code in the squirlley braces after the if will be executed. This sort of code block behavior is common to all statements.

What are the conditions?

i == 10;    /* true if i is equal to 10 */
i != 10;    /* true if i is not equal to 10 */
i > 10;     /* true if i greater than 10 */
i < 10;     /* true if i less than 10 */
i >= 10;    /* true if i greater than  or equal to 10 */
i <= 10;    /* true if i less than or equal to 10 */
i <= 10;    /* true if i less than or equal to 10 */

Guess what these all are? No really, guess. They're expressions! Just like before! So statements take an expression (some statements take multiple expressions) and evaluate them. The if statement evaluates to see if the expression is true, and then executes the following code if it is.

What is "true" anyway? C doesn't have a "true" keyword like C++ does. In C, any non-zero value is true, and a zero value is false. For instance:

if (1)     printf("This will always print.\n");
if (-3490) printf("This will always print.\n");
if (0)     printf("This will never print.  Ever.\n");

And the following will print 1 followed by 0:

int i = 10;

printf("%d\n", i == 10); /* i == 10 is true, so it's 1 */
printf("%d\n", i > 20);  /* i is not > 20, so this is false, 0 */

(Hey, look! We just passed those expressions as arguments to the function printf()! Just like we said we were going to do before!)

Now, one common pitfall here with conditionals is that you end up confusing the assignment operator (=) with the comparison operator (==). Note that the results of both operators is an expression, so both are valid to put inside the if statement. Except one assigns and the other compares! You most likely want to compare. If weird stuff is happening, make sure you have the two equal signs in your comparison operator.

3.4.2. The while statement

Let's have another statement. Let's say you want to repeatly perform a task until a condition is true. This sounds like a job for the while loop. This works just like the if statement, except that it will repeately execute the following block of code until the statement is false, much like an insane android bent on killing its innocent masters.

Or something.

Here's an example of a while loop that should clarify this up a bit and help cleanse your mind of the killing android image:

// print the following output:
//
//   i is now 0!
//   i is now 1!
//   [ more of the same between 2 and 7 ]
//   i is now 8!
//   i is now 9!

i = 0;

while (i < 10) {
    printf("i is now %d!\n", i);
    i++;
}

printf("All done!\n");

The easiest way to see what happens here is to mentally step through the code a line at a time.

  1. First, i is set to zero. It's good to have these things initialized.
  2. Secondly, we hit the while statement. It checks to see if the continuation condition is true, and continues to run the following block if it is. (Remember, true is 1, and so when i is zero, the expression i < 10 is 1 (true).
  3. Since the continuation condition was true, we get into the block of code. The printf() function executes and outputs "i is now 0!".
  4. Next, we get that post-increment operator! Remember what it does? It adds one to i in this case. (I'm going to tell you a little secret about post-increment: the increment happens AFTER all of the rest of the expression has been evaluated. That's why it's called "post", of course! In this case, the entire expression consists of simply i, so the result here is to simply increment i.
  5. Ok, now we're at the end of the basic block. Since it started with a while statement, we're going to loop back up to the while and then:
  6. We have arrived back at the start of the while statement. It seems like such a long time ago we were once here, doesn't it? Only this time things seem slightly different...what could it be? A new haircut, perhaps? No! The variable i is equal to 1 this time instead of 0! So we have to check the continuation condition again. Sure enough, 1 < 10 last time I checked, so we enter the block of code again.
  7. We printf() "i is now 1!".
  8. We increment i and it goes to 2.
  9. We loop back up to the while statement and check to see if the continuation condition is true.
  10. Yadda, yadda, yadda. I think you can see where this is going. Let's skip ahead to the incredible future where people commute to work on their AI-controlled rocket scooters, eat anti-gravity beets, and little spherical robot helicopters freely roam the skies making beep-beep-beep-beep noises. And where the variable i has finally been incremented so it's value is 10. Meanwhile, while we've slept in cryogenic hybernation, our program has been dutifully fulfilling its thousand-year mission to print things like "i is now 4!", "i is now 5!", and finally, "i is now 9!"
  11. So i has finally been incremented to 10, and we check the continuation condition. It 10 < 10? Nope, that'll be false and zero, so the while statement is finally completed and we continue to the next line.
  12. And lastly printf is called, and we get our parting message: "All done!".

That was a lot of tracing, there, wasn't it? This kind of mentally running through a program is commonly called desk-checking your code, because historically you do it sitting at your desk. It's a powerful debugging technique you have at your disposal, as well.

3.4.3. The do-while statement

So now that we've gotten the while statement under control, let's take a look at its closely related cousin, do-while.

They are basically the same, except if the continuation condition is false on the first pass, do-while will execute once, but while won't execute at all. Let's see by example:

/* using a while statement: */

i = 10;

// this is not executed because i is not less than 10:
while(i < 10) {
    printf("while: i is %d\n", i);
    i++;
}

/* using a do-while statement: */

i = 10;

// this is executed once, because the continuation condition is
// not checked until after the body of the loop runs:
do {
    printf("do-while: i is %d\n", i);
    i++;
} while (i < 10);

printf("All done!\n");

Notice that in both cases, the continuation condition is false right away. So in the while, the condition fails, and the following block of code is never executed. With the do-while, however, the condition is checked after the block of code executes, so it always executes at least once. In this case, it prints the message, increments i, then fails the condition, and continues to the "All done!" output.

The moral of the story is this: if you want the loop to execute at least once, no matter what the continuation condition, use do-while.

3.4.4. The for statement

Now you're starting to feel more comfortable with these looping statements, huh! Well, listen up! It's time for something a little more complicated: the for statement. This is another looping construct that gives you a cleaner syntax than while in many cases, but does basically the same thing. Here are two pieces of equivalent code:

// using a while statement:

// print numbers between 0 and 9, inclusive:
i = 0;
while (i < 10) {
    printf("i is %d\n");
    i++;
}

// do the same thing with a for-loop:
for (i = 0; i < 10; i++) {
    printf("i is %d\n");
}

That's right, kids--they do exactly the same thing. But you can see how the for statement is a little more compact and easy on the eyes.

It's split into three parts, separated by semicolons. The first is the initialization, the second is the continuation condition, and the third is what should happen at the end of the block if the contination condition is true. All three of these parts are optional. And empty for will run forever:

for(;;) {
    printf("I will print this again and again and again\n" );
    printf("for all eternity until the cold-death of the universe.\n");

}