learn c with me - week 4 - flow control pt.2

Table of contents

note on switch

In previous post I presented switch statement as an alternative for if else statement, but I forgot to mention one important aspect of switch; the default: case which is some sort of catch-all, for situations if checked value doesn't really fit into any other case.

                
#include <stdio.h>
int main(void)
{
    int x;
        printf("Please type a number here: ");
        scanf("%d", &x);
        switch(x)
        {         
            case 1:
                printf("you typed 1.\n");
                break;
            case 2:
                printf("you typed 2.\n");
                break;
            case 69:
                printf("tehehe\n");
                break;
            default:
                printf("it seems like the value provided by you is neither 1 nor 2, not 69 even.\n");
        }
        return 0;
    }
                
            

Output:

                
Please type a number here: 65537
it seems like the value provided by you is neither 1 nor 2, not 69 even.
Program ended with exit code: 0
                
            

loops

At some point you will like to write some code which will be repeated more than once. For example, you might want to print some sequence of number, like all hexadecimal digits or all odd numbers in range from 0 to 10 trillion. You can do the first one like this:

                
#include <stdio.h>
int main(void)
{
    int x = 0;
    if(x == 0)
    {
        printf("0x0, ");
        x++;
        if(x == 1)
        {
            printf("0x1, ");
            x++;
            if(x == 2)
            {
                printf("0x2, ");
                x++;
                if(x == 3)
                {
                    printf("0x3, ");
                    x++;
                    if(x == 4)
                    {
                        printf("0x4, ");
                        x++;
                        if(x == 5)
                        {
                            printf("0x5, ");
                            x++;
                            if(x == 6)
                            {
                                printf("0x6, ");
                                x++;
                                if(x == 7)
                                {
                                    printf("0x7, ");
                                    x++;
                                    if(x == 8)
                                    {
                                        printf("0x8, ");
                                        x++;
                                        if(x == 9)
                                        {
                                            printf("0x9, ");
                                            x++;
                                            if(x == 10)
                                            {
                                                printf("0xA, ");
                                                x++;
                                                if(x == 11)
                                                {
                                                    printf("0xB, ");
                                                    x++;
                                                    if(x == 12)
                                                    {
                                                        printf("0xC, ");
                                                        x++;
                                                        if(x == 13)
                                                        {
                                                            printf("0xD, ");
                                                            x++;
                                                            if(x == 14)
                                                            {
                                                                printf("0xE, ");
                                                                x++;
                                                                if(x == 15)
                                                                {
                                                                    printf("0xF\n ");
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    return 0;
}
                
            

Output:

                
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF
Program ended with exit code: 0
                
            

It works, but for what a cost? This code is WILD. We just wasted 85 lines to print just 16 different numbers.
This is called spaghetti code. This term is used to describe code which is poorly written, difficult to read and work with.
I assume that Dante specially created a separate circle in hell for people who code like that. Don't.
There is a better way to do that and it's called loops. Loops are conditional statements, repeating some task as long as conditions are met. Don't worry, it's easier than it sounds.

while loop

While loop is most basic type of the loop. It's repeating the code as long as expression passed into it evaluates to true (as mentioned in earlier posts, true is every value other than 0).
Here is how it looks:

                    
while(expression)
{
    //code block
}
                    
                

The previous program for writing all hexadecimal digits might be simplified using while loop like this:

                    
#include <stdio.h>
int main(void)
{
    int x = 0;
    while(x < 15)
    {
        printf("0x%X, ", x);
        x++;
    }
    printf("0x%X\n", x);
    return 0;
}
                    
                

Output:

                    
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF
Program ended with exit code: 0
                    
                

While loop might be also used in case we don't really know exact amount of iterations required to finish the task. To do this simply pass 1 instead of expression to while loop declaration like here:

                    
while(1)
{
    //code block
}
                    
                

Note: in this setting loop won't stop itself as previously. We have to manually decide where it should exit, by using break; statement, otherwise you will have to kill the process, if your operating system won't do it earlier.

                    
#include <stdio.h>
int main(void)
{
    int x = 0;
    while(1)
    {
        printf("%d\n", x);
        x++;
    }
    return 0;
}
                    
                

Output:

                    
0
1
2
...
54409
54410
54411
Message from debugger: killed
Program ended with exit code: 9
                    
                

As you might see, this program had to be killed by operating system, which is not nice.
Situation can escalate quickly, if you execute infinite loop in cloud environment where you are often billed per cpu instruction amount, which might be expensive mistake.
To avoid this think twice before creating "always on" loops like this and know exactly when they will stop, like below:

                    
#include <stdio.h>
int main(void)
{
    int x = 192;
    while(1)
    {
        printf("%d\n", x);
        x--;
        if(x == 2)
        {
            break;
        }
    }
    return 0;
}
                    
                

Output:

                    
192
191
190
...
5
4
3
Program ended with exit code: 0
                    
                

As you might see, this loop is being stopped every time when x value is equal to 2, which makes the countdown working as expected and there is no infinite loop bug.

do while

do while is another type of loop, but it's declared in bit different way than previous one; the condition is checked after executing the code block, which makes it run at least once no matter of condition and then repeated only if condition is met.

                    
do
{
    //code block
}
while(expression);
                    
                

This is especially useful when dealing with user input for example in cli programs with option menu like the one below:

                    
#include <stdio.h>
int main(void)
{
    int choice;
    do
    {
        printf("1. say hai!!:3\n2. say hello.\n3. Exit\nEnter your choice: ");
        scanf("%d", &choice);

        switch(choice)
        {
            case 1:
                printf("haiiii :3 hii!! haii!!!\n");
                break;
            case 2:
                printf("hello.\n");
                break;
            case 3:
                printf("Exiting the program\n");
                break;
            default:
                printf("Invalid choice. Try again.\n");
                break;
        }
    }
    while(choice != 3);
    return 0;
}
                    
                

for loop

For loop is final stage of loop evolution, intended to keep things nice and organised by keeping all these loop things in one place. Right now, creating loop was something like this:

                    
initialValue; //variable storing amount of executed iterations
while(condition) //most likely condition stopping loop after some number of iterations
{
    //code block
    initialValue++; //or virtually any other mathematical expression to keep track on iterations
}
                    
                

But now things changed. For loops are one stop solution for creating easy to maintain and read loops.

                
for(initialValue; condition; initialValue++)
{
    //code block
}
                
            

Now, you have to put all 3 expressions in one place, which minimises the risk of creating infinite loop and messing things up.

                
for(int i = 0; i < 10; i++)
{
    printf("the value of i is %d\n", i);
}
                
            

Output:

                
the value of i is 0
the value of i is 1
the value of i is 2
the value of i is 3
the value of i is 4
the value of i is 5
the value of i is 6
the value of i is 7
the value of i is 8
the value of i is 9
Program ended with exit code: 0
                
            

This makes it elegant way to iterate over for example lists of items or ranges of numbers.
Another example:

                
for(int i = 0, j = 0, n = 100; i < n; i++)
{
    if(i%2 == 0)
    {
        printf("#%d even number which is less than %d is %d\n", j, n, i);
        j++;
    }
}
                
            

This simple program prints all even numbers in desired range, where j is keeping track on amount of even numbers, i is number on which we are operating and n is upper bound of range in which we want to check numbers.

fun fact

Why i and j?
You are not forced to use i,j and k as iterators, but it's common in math and started long before first computers were invented. You can find use of them as iterators even in those 19th century books: The differential and integral calculus, 1816 and The differential and integral calculus, 1836. What's interesting, it was most likely influences by René Descartes who suggested using first letters of latin alphabet (a,b,c) as name for constants, the middle values (i,j,k,n) for integers and last letters (x,y,z) for unknown values.
Nowadays using i,j and k as iterators is widely recognisable practice and preferred way to write loops.

nested loops

Loops can be nested the same way as if statements.

                
int x = 0;
for(int i = 0; i < 100; i++)
{
    for(int j = 0; j < 100; j++, x++)
    {
        printf("The value of i is %d, the value of j is %d.\n", i, j);
    }
}
printf("the total number of iterations is %d\n", x);
                
            

Output:

                
The value of i is 0, the value of j is 0.
The value of i is 0, the value of j is 1.
The value of i is 0, the value of j is 2.
...
The value of i is 99, the value of j is 97.
The value of i is 99, the value of j is 98.
The value of i is 99, the value of j is 99.
the total number of iterations is 10000
Program ended with exit code: 0
                
            

In this example for every 1 iteration of i the 100 iterations of j has to be performed, leading to total of 10 000 operations done by this loop.
It's worth keeping in mind, that amount of operations done by nested loops is product of multiplication rather than result of a sum. That's why it's always good idea to think whether we really need to use nested loop, to avoid any potential performance issues.

exercise

Through the course of this post we gained good foundation for writing interactive and decisive code, so let's put our skills into test.
Remember the tool we created previously for converting temperature from Kelvin to Fahrenheit? I think now it's good opportunity to expand its functionality even further by adding more conversion options.
I think it will be nice to add also other units, such as conversion from miles to meters and from liters to table spoons.

As always, let's start from main function

                
#include <stdio.h>
int main(void)
{       
    return 0;
}
                
            

And now, inside main function create a switch for accessing every single function along with option variable.
For now I assume that 1 will be for conversion from Kelvin to Fahrenheit; 2 will be for conversion from miles to meters; 3 will be for conversion from litres to table spoons; 4 will be for exiting the program and the last one will be catch-all statement for detecting invalid input.

                
int option;
switch(option)
{
    case 1:

    case 2:

    case 3:

    case 4:

    default:
    
}
                
            

Now it's time to import standard input and output library to process user input and display main menu.

                
#include <stdio.h>
int main(void)
{
    int option;
    printf("-------------------------\nUnit Conversion Program\n1. Kelvin to Fahrenheit\n2. Miles to Meters\n3. Liters to Table Spoons\n4. exit\n-------------------------\nPlease choose an option from 1 to 4: ");
    scanf("%d", &option);
    return 0;
}   
                
            

Let's go back to switch statement and add logic for every single option.

                
//declare all variables required for data conversion outside switch
float kTemperature, fTemperature, miles, meters, litres, tablespoons;

switch(option)
{
    case 1:
        printf("Please type the Kelvin value to be converted: ");
        scanf("%f", &kTemperature);
        fTemperature = (kTemperature - 273.15) * 9/5 + 32;
        printf("Converted %.2fK to %.2fF successfully.\n", kTemperature, fTemperature);
    case 2:
        printf("Please type the amount of miles to be converted: ");
        scanf("%f", &miles);
        meters = miles * 1609.344;
        printf("Converted %.2f miles to %.2f meters successfully.\n", miles, meters);

    case 3:
        printf("Please type the number of litres to be converted: ");
        scanf("%f", &litres);
        tablespoons = litres * 67.62804;
        printf("Converted %.2f litres to %.2f table spoons successfully.\n", litres, tablespoons);

    case 4:
        printf("Exiting program.\n");
        return 0;

    default:
        printf("Please choose valid option.\n");
    
}
                
            

Now all pieces of code can be connected together.

                
#include <stdio.h>
int main(void)
{
    //declare all variables required for data conversion outside switch
    float kTemperature, fTemperature, miles, meters, litres, tablespoons;
    int option;

    //show main menu
    printf("-------------------------\nUnit Conversion Program\n1. Kelvin to Fahrenheit\n2. Miles to Meters\n3. Liters to Table Spoons\n4. exit\n-------------------------\nPlease choose an option from 1 to 4: ");

    //check user input and perform conversion
    scanf("%d", &option);

    switch(option)
    {
        case 1:
            printf("Please type the Kelvin value to be converted: ");
            scanf("%f", &kTemperature);
            fTemperature = (kTemperature - 273.15) * 9/5 + 32;
            printf("Converted %.2fK to %.2fF successfully.\n", kTemperature, fTemperature);
        case 2:
            printf("Please type the amount of miles to be converted: ");
            scanf("%f", &miles);
            meters = miles * 1609.344;
            printf("Converted %.2f miles to %.2f meters successfully.\n", miles, meters);

        case 3:
            printf("Please type the number of litres to be converted: ");
            scanf("%f", &litres);
            tablespoons = litres * 67.62804;
            printf("Converted %.2f litres to %.2f table spoons successfully.\n", litres, tablespoons);

        case 4:
            printf("Exiting program.\n");
            return 0;

        default:
            printf("Please choose valid option.\n");
        
    }
    return 0;
}
                
            

Now it's time to test the program we just created.

                
-------------------------
Unit Conversion Program
1. Kelvin to Fahrenheit
2. Miles to Meters
3. Liters to Table Spoons
4. exit
-------------------------
Please choose an option from 1 to 4: 3
Please type the number of litres to be converted: 12
Converted 12.00 litres to 811.54 table spoons successfully.
Exiting program.
Program ended with exit code: 0
                
            

It seems like it's working, but it's not repeating itself until user explicitly wants to exit as it should. To change that, we have to add do while statement repeating code as long as option 4 (exit) is not chosen.

                
#include <stdio.h>
int main(void)
{
    //declare all variables required for data conversion outside switch
    float kTemperature, fTemperature, miles, meters, litres, tablespoons;
    int option;

    do
    {
        //show main menu
        printf("-------------------------\nUnit Conversion Program\n1. Kelvin to Fahrenheit\n2. Miles to Meters\n3. Liters to Table Spoons\n4. exit\n-------------------------\nPlease choose an option from 1 to 4: ");

        //check user input and perform conversion
        scanf("%d", &option);

        switch(option)
        {
            case 1:
                printf("Please type the Kelvin value to be converted: ");
                scanf("%f", &kTemperature);
                fTemperature = (kTemperature - 273.15) * 9/5 + 32;
                printf("Converted %.2fK to %.2fF successfully.\n", kTemperature, fTemperature);
            case 2:
                printf("Please type the amount of miles to be converted: ");
                scanf("%f", &miles);
                meters = miles * 1609.344;
                printf("Converted %.2f miles to %.2f meters successfully.\n", miles, meters);

            case 3:
                printf("Please type the number of litres to be converted: ");
                scanf("%f", &litres);
                tablespoons = litres * 67.62804;
                printf("Converted %.2f litres to %.2f table spoons successfully.\n", litres, tablespoons);
            case 4:
                printf("Exiting program.\n");
                return 0;
            default:
                printf("Please choose valid option.\n");
            
        }
    }while(option != 4);
    return 0;
}
                
            

Output:

                
-------------------------
Unit Conversion Program
1. Kelvin to Fahrenheit
2. Miles to Meters
3. Liters to Table Spoons
4. exit
-------------------------
Please choose an option from 1 to 4: 6
Please choose valid option.
-------------------------
Unit Conversion Program
1. Kelvin to Fahrenheit
2. Miles to Meters
3. Liters to Table Spoons
4. exit
-------------------------
Please choose an option from 1 to 4: 1
Please type the Kelvin value to be converted: 255
Converted 255.00K to -0.67F successfully.
Please type the amount of miles to be converted:
Message from debugger: killed
Program ended with exit code: 9
                
            

The code is repeating itself as intended, but why it started conversion to miles instead of showing menu again? It seems like we forgot to use break; at the end of each case. Let's fix it quickly.

                
#include <stdio.h>
int main(void)
{
    //declare all variables required for data conversion outside switch
    float kTemperature, fTemperature, miles, meters, litres, tablespoons;
    int option;

    do
    {
        //show main menu
        printf("-------------------------\nUnit Conversion Program\n1. Kelvin to Fahrenheit\n2. Miles to Meters\n3. Liters to Table Spoons\n4. exit\n-------------------------\nPlease choose an option from 1 to 4: ");

        //check user input and perform conversion
        scanf("%d", &option);

        switch(option)
        {
            case 1:
                printf("Please type the Kelvin value to be converted: ");
                scanf("%f", &kTemperature);
            fTemperature = (kTemperature - 273.15) * 9/5 + 32;
                printf("Converted %.2fK to %.2fF successfully.\n", kTemperature, fTemperature);
                break;
            case 2:
                printf("Please type the amount of miles to be converted: ");
                scanf("%f", &miles);
                meters = miles * 1609.344;
                printf("Converted %.2f miles to %.2f meters successfully.\n", miles, meters);
                break;
            case 3:
                printf("Please type the number of litres to be converted: ");
                scanf("%f", &litres);
                tablespoons = litres * 67.62804;
                printf("Converted %.2f litres to %.2f table spoons successfully.\n", litres, tablespoons);
                break;
            case 4:
                printf("Exiting program.\n");
                break;
                return 0;   
            default:
                printf("Please choose valid option.\n");
                break;  
        }
    }while(option != 4);
    return 0;
}
                
            

Now everything works as expected! Yippiee!

                
-------------------------
Unit Conversion Program
1. Kelvin to Fahrenheit
2. Miles to Meters
3. Liters to Table Spoons
4. exit
-------------------------
Please choose an option from 1 to 4: 6
Please choose valid option.
-------------------------
Unit Conversion Program
1. Kelvin to Fahrenheit
2. Miles to Meters
3. Liters to Table Spoons
4. exit
-------------------------
Please choose an option from 1 to 4: 3
Please type the number of litres to be converted: 12
Converted 12.00 litres to 811.54 table spoons successfully.
-------------------------
Unit Conversion Program
1. Kelvin to Fahrenheit
2. Miles to Meters
3. Liters to Table Spoons
4. exit
-------------------------
Please choose an option from 1 to 4: 2
Please type the amount of miles to be converted: 2
Converted 2.00 miles to 3218.69 meters successfully.
-------------------------
Unit Conversion Program
1. Kelvin to Fahrenheit
2. Miles to Meters
3. Liters to Table Spoons
4. exit
-------------------------
Please choose an option from 1 to 4: 4
Exiting program.
Program ended with exit code: 0
                
            

summary

As you might see, C language is not as scary as it looks. In just one month we went from complete zero to creating fully working silly little but fully working cli programs!
What's more, it's just a tip of the iceberg in terms of C capabilities and in next post we will go even deeper into coding. In fact, the adventure is just beginning.

As always, I want to thank all people giving me advises, reviewing my garbage code or just sitting and listening to my monologues when I tried to figure things out aloud. Without people like you, this series wouldn't even exist.

This is all I wanted to say for today.
Have a nice weekend and don't forget about proper hydration
Laura