Road to C Programmer #2 - The Very Basics Cont'd

Last Edited: 7/16/2024

The blog post continues to introduce the very basics of C.

C Basics 2

Bear with me for a little bit. Learning about these basics carefully is instrumental for the future.

If Statements

Let's say we are trying to give comments to students depending on the letter grade. We can do so using if statements as follows:

char grade = 'A';
 
if (grade == 'A') {
    printf("Perfect!");
} else if (grade == 'B') {
    printf("Great!");
} else if (grade == 'C') {
    printf("Great. ");
} else if (grade == 'D') {
    printf("Great. ");
} else if (grade == 'E') {
    printf("You survived. ");
} else if (grade == 'F') {
    printf("You failed. ");
} else {
    printf("Grade is invalid.");
}

However, the above is not ideal because we have three conditions that print the same thing. This can be simplified using the AND logical operator &&.

char grade = 'C';
 
if (grade == 'A') {
    printf("Perfect!");
} else if ('B' <= grade && grade <= 'D') {
    printf("Great!");
} else if (grade == 'E') {
    printf("You survived. ");
} else if (grade == 'F') {
    printf("You failed. ");
} else {
    printf("Grade is invalid.");
}

In addition to the AND operator, we have the OR operator || and the NOT operator !. Inside the if statement, we can use < and > for greater than or less than, == for equal to, and != for not equal to.

Switch Statements

However, having many else if is regarded as bad coding practice. When there are many conditions we need to check, we use a switch statement.

switch (grade) {
    case 'A':
        printf("Perfect!");
        break;
    case 'B':
        printf("Great!");
        break;
    case 'C':
        printf("Nice. ");
        break;
    case 'D':
        printf("OK. ");
        break;
    case 'E':
        printf("You survived. ");
        break;
    case 'F':
        printf("You failed. ");
        break;
    default:
        printf("Grade is invalid. ");
}

We need to use break to stop the execution of the switch statement. Otherwise, the programs in the following cases will also get triggered.

Ternary Operators

The following is a valid code, but writing this all the time is quite tedious.

int a = 4;
int b = 3;
 
int min;
if (a < b) {
    min = a;
} else {
    min = b;
}
 
printf("Minimum: %d", min);

Instead, we can use ternary operator to refactor the code as follows.

int a = 4;
int b = 3;
 
// Ternary Operator (condition) ? value if true : value if false;
int min = (a < b) ? a : b; 
 
printf("Minimum: %d", min);

This can simplify the code in many scenarios.

For Loops

When we want to have for loops in C, we can do so as follows.

// For Loop 
// for (starting index; condition to continue; how to increment index) { operation }
for (int i = 1; i <= 10; i++) {
    printf("%d ", i);
} 
// => 1 2 3 4 5 6 7 8 9 10

We can also decrement as well.

// For Loop, Decrement
for (int i = 10; i >= 1; i--) {
    printf("%d ", i);
} 
// => 10 9 8 7 6 5 4 3 2 1
 
// For Loop, Decrement by 2
for (int i = 10; i >= 1; i-=2) {
    printf("%d ", i);
} 
// => 10 8 6 4 2

While Loops

When we want to run a program indefinitely until a condition is met, we can do so with while loop.

int i = 1;
 
while (i <= 10) {
    printf("%d ", i);
    i++;
} 
// => 1 2 3 4 5 6 7 8 9 10

There are some situations where we want to perform an operation first and decide whether to iterate it again. In such scenario, we use a do-while loop.

int age;
 
do {
    printf("What is your age?: ");
    scanf("%d", &age);
} while (age < 0);
 
printf("Your age is %d", age);

We can ask for age first and decide whether we continue asking depending on the user input.

Break vs Continue

A lot of beginners seem to get very confused about the difference between break and continue, so here is the explanation. While break breaks out of the loop or terminates the loop that it is in, continue skip one iteration of the loop. Let's see some examples to understand what I mean by that.

// Break
for (int i = 1; i <= 5; i++) {
    if (i == 3) {
        break;
    } else {
        printf("%d ", i);
    }
}
// => 1 2
 
// Continue
for (int i = 1; i <= 5; i++) {
    if (i == 3) {
        continue;
    } else {
        printf("%d ", i);
    }
}
// => 1 2 4 5

In the above example, when break is used, the output is 1 2 because break terminated the loop it is in. On the other hand, when continue is used, the output is 1 2 4 5 because continue just skipped 3. Both the break and continue only work on the loop on the same level even when the loop is nested.

// Break (exit the inner loop when i == j)
for (int i = 1; i <= 5; i++) {
    for (int j = 1; j <= 5; j++) {
        if (i == j) {
            break;
        } else {
            printf("%d ", j);
        }
    }
    printf("\n");
}
// => 1
//    1 2
//    1 2 3
//    1 2 3 4
 
// Continue (skip when i == j but continue the inner loop)
for (int i = 1; i <= 5; i++) {
    for (int j = 1; j <= 5; j++) {
        if (i == j) {
            continue;
        } else {
            printf("%d ", j);
        }
    }
    printf("\n");
}
// => 2 3 4 5
//    1 3 4 5
//    1 2 4 5
//    1 2 3 5
//    1 2 3 4

Arrays

You have seen how strings, arrays of chars, can be initialized. How can we do that for other datatypes? We can do that like the following.

int grades[] = {1,2,3,4,5};

When we want to access the elements of an array, we can do so as follows.

grades[0]; // => 1
grades[1]; // => 2
grades[2]; // => 3

When we want to iterate over an array, we can calculate the number of elements with sizeof.

// Getting the number of elements
// We are dividing the size of the whole list by the size of the first element
// It works because the datatype of elements in an array is consistent
 
int size = sizeof(grades)/sizeof(grades[0]);
 
for (int i = 0; i < size; i++) {
    printf("Grade: %d\n", grades[i]);
}

You have to use < instead of <= because an array is 0 indexed in C.

Functions

having all the code in main function is a bad idea when the code is long and contains repetition. To simplify the code, we can define other functions before main like the following.

// Functions  outputType functionName (Inputs) { Operations }
void happyBirthday (char name[], int age) {
    printf("Happy Birthday, %s!\n", name);
    printf("You just turned %d years old!\n", age)
}
 
int square (int x) {
    return x*x;
}
 
int main () {
    char name[] = "Micheal Jordan";
    int age = 24;
 
    happyBirthday(name, age);
 
    int ageSquared = square(age);
    printf("Your age squared is %d",ageSquared);
 
    return 0;
}

When we want to define other functions after main for readability, we need to be careful that many C compilers do not check parameter matching when functions are not defined before they are invoked, which can result in unexpected behavior. Hence, we want to add function prototypes at the beginning for compilers to properly check parameter matching.

// Function prototypes
void happyBirthday (char[], int);
int square (int);
 
int main () {
    char name[] = "Micheal Jordan";
    int age = 24;
 
    happyBirthday(name);
 
    int ageSquared = square(age);
    printf("Your age squared is %d",ageSquared);
 
    return 0;
}
 
void happyBirthday (char name[], int age) {
    printf("Happy Birthday, %s!\n", name);
    printf("You just turned %d years old!\n", age)
}
 
int square (int x) {
    return x*x;
}

Exercises

From this article, there will be an exercise section where you can test your understanding of the material introduced in the article. I highly recommend solving these questions by yourself after reading the main part of the article. You can click on each question to see its answer.

Resources