The University of Queensland Homepage
School of ITEE ITEE Main Website

 CSSE1000/CSSE7035 C Programming Tutorial

2. If Statements, Loops, Expressions and Functions

This tutorial introduces you to conditional statements (if statements) and loops. Along the way, you're also introduced to variable declarations, assignment statements, and expressions. The tutorial also introduces the concept of functions.

Assumed Background

It is assumed that you have undertaken tutorial 1 and, ideally, have read one of the associated readings.

Associated Reading

The following textbook sections are relevant:

  • Kernigan & Ritchie (2nd ed.) - pages 8 to 14, sections 1.7 to 1.10, 4.1 to 4.4, 4.9 (we haven't covered all of these concepts yet)
  • Harbison & Steele - second chapter (it covers much more than this tutorial - please read selectively), 4.1 to 4.6, 5.1 to 5.2, 7.1 to 7.12, 9.1 (we haven't covered all of these concepts yet)

Other material within this text is also relevant.

An online C Reference Manual (by Martin Leslie) is also available. Relevant sections include (but are not limited to):

Start the Development Environment

Start the development environment as described in tutorial 1. You may wish to create a new project for this tutorial. You may type (cut and paste) each program in a separate C file or you may just use one C file and just overwrite your previous program each time.

Conditional statements

Most programs need to make decisions based on the data they're operating on. For example, consider the following program:

#include <stdio.h>

int main() {
    int num;
    
    /* Prompt for and read in number from user */
    printf("Enter number: ");
    scanf("%d", &num);
    if((num % 2) == 1) {
        printf("%d is odd\n", num);
    } else {
        printf("%d is even\n", num);
    }
    return 0;
}

You should type this program into the editor (or copy and paste it in) and run it. There are several new concepts illustrated in this program:

if ...
This is a conditional statement that takes the general form:
if(expression1) {
    statement1;
}
else if (expression2) {
    statement2;
}
...
else {
    statementn;
}
If the first expression is true, then the first statement is executed. If the second expression is true then the second statement is executed, and so on. If no expression is true, the last statement is executed. Note that the else parts of the statement are optional. Note also that any of the statements can actually be multiple statements enclosed in braces. It is good programming style to always use the braces as in the example above. C interprets an expression of value 0 as false and any expression with non-zero value as true.
num % 2
The % or modulus operator returns the remainder of dividing the integer on the left by the integer on the right, i.e. x % y produces the remainder when x is divided by y (e.g. 11 % 3 is 2, 12 % 3 is 0). Other arithmetic operators include + (addition), - (subtraction), * (multiplication) and / (division). Note that / (division) applied to integers will produce the quotient - i.e. any remainder will be thrown away (e.g. 11/3 = 3). Use the % operator to obtain the remainder. The +, -, * and / operators can be applied to floating point numbers, however the % operator can not.
(num % 2) == 1
The == operator performs an equality comparison between the expression on the left (num % 2 in this case) and that on the right (1 in this case) and produces true (i.e.  a non-zero value) if they're equal and false otherwise (i.e. 0).
printf("%d is odd\n", num);
We used printf() in tutorial 1 to print "Hello World" and other strings. The printf() function takes a variable number of arguments. The first argument is a string of characters (formatting string) to be printed with each % indicating where the other arguments are to be substituted (and the character after the percent indicating the type of the argument). For example, %d indicates an integer. You must ensure that the type of the argument matches that specified in the formatting string.
Note that printf() is not part of the C language - it's just a function defined in a standard library of functions. Not all implementations will have a printf() function. We'll see further examples of printf() statements below.

Exercise

1. Modify the program above to print whether a number is divisible by 7 or not.

Loops

A "loop" is a programming construct that lets you repeat a number of actions a certain number of times (or until a certain condition is true or false). We're going to look at several ways of writing loops in C.

Consider the following flowchart which illustrates how to add the integers 1 through 10 and output the result:

The C equivalent of this program is:

#include <stdio.h>

int main() {
    int num, sum;

    num = 1;
    sum = 0;
    while(num <= 10) {
        sum = sum + num;
        num = num + 1;
    }
    printf("Sum = %d\n", sum);
    return 0;
}

You should type this program into the editor (or copy and paste it in) and run it. There are several new (as well as some already familiar) concepts illustrated in this program:

int num, sum;
Note that multiple variables of the same type can be declared on the same line.
The declaration could have been written as two lines:
int num;
int sum;
num = 1;
This line is an example of an assignment statement. The variable on the left of the equals sign is assigned the value of the expression on the right. Assignment statements take the form:
variable-name = expression;
while(num <= 10) {
    ...
}
This is a while-loop. The program will execute the statements within the loop while the expression within the parentheses is true (i.e. in this case, while the value of the variable num is less than or equal to 10). While-loops take the form
while(expression) {
    statement;
    ...
}

and result in the execution of the statements (called the body of the loop) until the expression becomes true.
The braces may be omitted if there is only one statement within the loop:
while(expression)
    statement;

but it is usually safer to include them.
Note that it is conventional to indent the body of a loop to indicate the logical structure of the program, however, the C compiler doesn't care about indentation.
sum = sum + num;
num = num + 1;
These are further examples of assignment statements. The variable on the left is assigned the value of the expression on the right. Because it is quite common to add some value to a number, C has a special way of writing this. These statements could be rewritten as:
    sum += num;
    num += 1;

Further, because adding one to a number is quite common in C, it is possible to rewrite num += 1 as
    num++;
or
    ++num;
The ++ means increment by one and can be used as a postfix operator (after the variable name) or prefix operator (before the variable name). These can be used within expressions and have different values, e.g.
    sum += (num++);
means
    sum = sum + num;
    num = num + 1;

whereas
    sum += (++num);
means
    num = num + 1;
    sum = sum + num;

A similar operator (--) exists for decrementing (i.e. subtracting one).

Exercises

2. Modify the program above to calculate the factorial of 7 - i.e. multiply all integers from 1 to 7. Note that the *= operator can also be used in a similar way to the += operator.
3. Modify the program further so that it reads a number from the user and calculates the factorial of that number. Try running this program multiple times and determine the largest number for which you get a "reasonable" answer for the factorial. Why do you stop getting "reasonable" answers at some point?

For Loops

Another way of writing loops is with a for-loop statement. Consider the following program (type this into the editor and run it):

#include <stdio.h>

int main() {
    int num, sum;

    sum = 0;
    for(num = 0; num <= 10; num++) {
        sum += num;
    }
    printf("Sum = %d\n", sum);
    return 0;
}

This program does exactly the same thing as the while loop example above. For loops take the form:
    for(initialisation; test-expression; increment)
        statement
where statement can be replaced by any number of statements within braces. There are three parts within the parentheses - separated by semicolons. The first part is the initialisation - performed once before the loop is entered. The second part is the test expression - the body of the loop is executed when this expression is true. If the body of the loop is executed, the increment part is then executed and the test expression is re-evaluated. (Note that the increment step does not have to increment by 1 - any expression can be used here.

To add the odd numbers from 1 to 99, the for loop above could be modified to:
    for(num=1; num <= 99; num = num+2) {
        sum += num;
    }

Note that a for loop can always be written as a while loop:
    initialisation;
    while(test-expression) {
        statement(s);
        increment;
    }

The choice between using a while loop and a for loop is somewhat arbitrary. Often a for loop is used when a fixed sequence is being counted through.

Loops can be nested. Type the following into the editor and run it:

#include <stdio.h>

int main() {
    int a, b;
    int size;
	
    size=10;
	
    for(a=1; a <= size; a++) {
	     for(b=1; b<= size; b++) {
	        /* Print out the sum of a and b - using a field width of 3 */
	        printf("%3d ", a+b);
	     }
	     printf("\n");
    }
    return 0;
}

The only new concept here is the use of a field width in the printf function. %3d means print out an integer using a minimum field width of 3 characters - i.e. if the number will take fewer than 3 characters when printed then pad it out with spaces so that it takes 3 characters.

This program produces an addition table for the first 10 integers:

  2   3   4   5   6   7   8   9  10  11 
  3   4   5   6   7   8   9  10  11  12 
  4   5   6   7   8   9  10  11  12  13 
  5   6   7   8   9  10  11  12  13  14 
  6   7   8   9  10  11  12  13  14  15 
  7   8   9  10  11  12  13  14  15  16 
  8   9  10  11  12  13  14  15  16  17 
  9  10  11  12  13  14  15  16  17  18 
 10  11  12  13  14  15  16  17  18  19 
 11  12  13  14  15  16  17  18  19  20 

We can extend the program to add some headings. Work through this program to make sure you understand how it works.

#include <stdio.h>

int main() {
    int a, b, i, size;

    size=10;

    /* Write out heading line */
    printf("a+b| ");
    for(b=1; b <= size; b++) {
        printf("%3d ", b);
    }
    printf("\n");

    /* Write out separator line (dashes) */
    printf("---+-");
    for(i=1; i <= size*4; i++) {
        printf("-");
    }
    printf("\n");
    for(a=1; a <= size; a++) {
        printf("%2d | ", a);
        for(b=1; b<= size; b++) {
            printf("%3d ", a+b);
        }
        printf("\n");
    }
    return 0;
}

Exercises

4. Modify the program above to produce a multiplication table.
5. Try modifying the field width specifications so that the table is slightly more spread out. What other lines have to change?
6. Modify the program to produce an addition table for numbers from -5 to 5 inclusive (rather than 1 to 10)
7. Modify the program to use while loops instead of for loops.

8. Write a C program to model the combination lock design from Learning Lab 10. The program prompts the user to enter two single digits (separately). Once the two digits have been entered they are printed on the screen. If the two digits entered are the correct combination "83" then a message is printed to the screen informing the user that the lock is now open. If the code entered is incorrect, a message is printed informing the user that the lock is closed.

Functions

A function is a self-contained part of a program which carries out some well-defined task. One function we've seen so far is printf() - for printing out formatted strings. This function has been provided for us in the Standard C library. In this tutorial we will look at functions written by the programmer.

Functions allow a program to be organised in well defined 'chunks'. Consider the following program which defines a function that calculates powers of integers (e.g. 3 to the power of 2). Type (or cut and paste) this in and run it.

#include <stdio.h>

int power(int base, int n) {
    int p;

    p = 1;
    while(n > 0) {
        p = p * base;
        n--;
    }
    return p;
}

int main() {
    int i;
    int powOf2;

    for (i=0; i < 10; i++) {
        powOf2 = power(2,i);
        printf("%d %d %d\n", i, powOf2, power(-3, i));
    }
    return 0;
}

Elements of this program are explained below:

int power(int base, int n) {
This line begins the function definition. The first "int" is the return type of the function (i.e. an integer). This is followed by the name of the function (power) and then a list of function arguments or parameters. In this case, the power function takes two arguments - both integers. The function definition includes a group of statements within braces {}.
int p;
This declares a variable - known as a local variable - it is only accessible within the function. Local variables come into existence when the function is called and are deallocated after the function returns. Local variables and the concept of scope are discussed further below. Note also that the argument variables (base and n) are local variables - they come into existence when the function is called.
return p;
This returns the value of p as the result of the function. (Note that it is legal to have multiple return statements in a function - e.g. you might write something like:
if (expression) {
    return value_1;
} else {
    return value_2;
}
power(2,i)
power(-3,i)
These are function calls - which cause the function to be evaluated using the values of the given arguments. In one case above (power of 2), we assign the value to a variable (powOf2) before passing it as an argument to printf. In the other case (power of -3), we pass the value directly to printf. The choice we have made is arbitrary.

In this example, the function power() is defined before it is used (within the main() function). This isn't always possible - so in these circumstances it is necessary to at least declare the function before it used. A function declaration (also called a function prototype) for power() would look like:

int power(int base, int n);
or
int power(int, int);

i.e. it specifies the name of the function, the types of the parameters and the return type. Note the semicolon at the end rather than opening braces to begin the function definition. (It is not necessary to include the parameter names - and they do not need to match the names used in the function definition. It is useful to include parameter names, however, as a reminder to the programmer as to what arguments are expected. For example, if we wanted to define the main() function before other functions (some programmers prefer to do this) then we'd need to include such a function prototype before main():

#include <stdio.h>

int power(int base, int exponent);

int main() {
    int i;
    int powOf2;

    for (i=0; i < 10; i++) {
        powOf2 = power(2,i);
        printf("%d %d %d\n", i, powOf2, power(-3, i));
    }
    return 0;
}

int power(int base, int n) {
    int p;

    p = 1;
    while(n > 0) {
        p = p * base;
        n--;
    }
    return p;
}

Exercises

9. Modify the program above so that it produces the same output, but does NOT assign the power of 2 to a variable before printing it.
10. Write a program that asks the user for a number base (integer) and then prints out the first 16 powers of that number (i.e. n0 to n15). You should be able to reuse the power() function - this is why functions are useful.

Void

Some functions don't return anything - in which case they are declared with a return type of void. (We'll see an example below.) Also, in some circumstances, the return type of a function is ignored. For example, the printf() function always returns the number of characters output but because we don't do anything with the return value (e.g. we don't assign it to a variable) this value is lost.

Passing by Value

Note that when a function is called, the arguments are copied into new local variables - i.e. we pass the value of the argument to the function - rather than the variable itself. Any changes made to the local variable affect only that variable and not the original source of the argument, e.g. in the example above, we pass the value of variable i as the second argument to the power() function. Each time the function is called, this value is assigned to a new local variable (called n). Changes to n within the function do not cause the value of i (in the main() function) to change.

Finishing up

When you're finished, quit wedit by selecting Quit from the File menu.

Make sure you read through the introductory material (K&R or H&S) listed above.