4. Cycles
By Joker. February 12th, 2020. 5:29 PM
Check the guide's index here.
Objectives- While, for and do ... while cycles
- Break and continue instructions
- Chained cycles
- Infinite cycles
- ++ and -- Operators
- Complex attributions +=, -=, *=, /=, %=
Intro
In the first chapters of this guide, a program was nothing but a sequence of instructions being executed. In the previous chapter, we took a new approach to writing programs, where instructions can be executed or not under one or more predefined conditions. A program's flux control can, then, be written through the if and switch clauses, and through other instructions that will be presented in this chapter, and that allow the repetition of instructions. Such instructions, for the role they play, are normally called Flux Control Instructions and include if, switch and the while, for and do ... while cycles.
The while clause (also called a while cycle) executes an instruction or block of instructions while a certain condition is true. Its syntax is:
while(condition) instruction;
The way it functions can be summarized like this:
- The condition is analyzed.
- If the result of the analysis is False (0-zero), the cycle ends and the program continues on the instruction that immediately follows the while.
- If the result of the analysis is True (different from zero), the instruction (or block of instructions) associated to the while is executed.
- Back to square 1.
Problem: Write a program that writes the first 10 integers on-screen.
The output we want on our screen is this:
1 2 3 4 5 6 7 8 9 10
prog0401.c
1: #include <stdio.h> 2: 3: int main(){ 4: int i = 1; 5: while(i <= 10){ 6: printf("%d\n", i); 7: i = i+1; 8: } 9: return 0; 10: }
To solve this problem, let's start by declaring an integer type variable that we initialize with the value 1 and that will be written on-screen while its value is lower or equal to 10 (which is the condition present in our while clause).
After writing that variable through the printf function, we need to increment it one unit, so it can move on to the next value.
Notice that the condition's test is executed before executing the instruction block.
Also take note that we need to create a block inside our while clause, because we need to execute two distinct instructions (printf and the variable incrementing) in each iteration of the cycle.
Question: What would be the output if there were no brackets in the while's instruction slot?
Answer: Motherfuck. If you know the way instruction blocks work, then why are you asking me this? It would make an infinite cycle that would always print the number 1, because the cycle's control variable would never be altered, so the cycle's condition would always be True. Stop being so dense.
It's already obvious if you notice, but the variable that controls the cycle needs to be altered within that cycle so that is allowed to end at some point in the runtime.
Now tell me - what does this next program do?
prog0402.c1: #include <stdio.h> 2: 3: int main(){ 4: int n = 10; 5: while(n){ 6: printf("%d\n", i); 7: n = n - 1; 8: } 9: return 0; 10: }
Let's explain this step-by-step:
- The variable
n
is initiated with the value 10. - The while condition is represented by the variable
n
, i.e., the while condition is true as long as n presents a value different from zero (since zero = False). That way, writing while(n) or while(n != 0) is exactly the same. - If
n
has a value different from zero, the condition is True and both instructions inside the while block are executed - we write the the value ofn
and right after that,n
is decremented. - When
n
becomes zero, the program ends. The number zero is not printed out, because since zero represents the logic value False, the cycle ends, and in this case, the program ends, because there are no other instructions after our while cycle.
In short, the previous program prints out on-screen the first 10 numbers in from highest to lowest.
Problem: Write a program that prints on-screen 5's multiplication table.
This is what should be printed on-screen:
5 * 1 = 5 5 * 2 = 10 5 * 3 = 15 5 * 4 = 20 5 * 5 = 25 5 * 6 = 30 5 * 7 = 35 5 * 8 = 40 5 * 9 = 45 5 * 10 = 50
Analysing the problem, we can verify that the first column always has the value of 5.
Next, we have a column that varies betweeb 1 and 10 (which corresponds to the number of times we need to iterate).
In the last column, we present the result of the multiplication of 5 by the current value of the cycle's control variable. The resulting program is this:
prog0403.c1: #include <stdio.h> 2: 3: int main(){ 4: int n = 1; 5: while(n <= 10){ 6: printf("5 * %2d = %2d\n", n, 5*n); 7: n = n + 1; 8: } 9: return 0; 10: }
Notice that the values are written through %2d, so that two characters are reserver to represent the number. If the number does not occupy the number of indicated characters (2), spaces on the left are placed, avoiding a disalignment on the value printing.
Problem: Rewrite the previous program so that it presents the multiplication table for any number inputted by the user.
prog0404.c1: #include <stdio.h> 2: 3: int main(){ 4: int n = 1, num; 5: printf("Input a number: "); scanf("%d", &num); 6: while(n <= 10){ 7: printf("%2d * %2d = %2d\n", num, n, num*n); 8: n = n + 1; 9: } 10: return 0; 11: }
The solution is simple, and consists in changing all occurrences of the constant 5 for the new variable num
. The printf invoking should then be adapted, substituting the value 5 for the writing format of the integer input by the user.
Problem: Write the set of the first 5 multiplication tables (multiplication tables for 1, 2, 3, 4 and 5).
The processing of a generic multiplication table, stored in the variable num, is the following code:
int n = 1, num; while(n <= 10){ printf("%2d * %2d = %2d\n", num, n, num*n); n = n + 1; }
The previous strip of code shows the complete multiplication table of a number stored in the variable num. Since we want to show the first 5 multiplication tables, we want that number (num
) to vary between 1 and 5.
1: #include <stdio.h> 2: 3: int main(){ 4: int n, num; 5: while(num <= 5){ 6: n = 1; 7: while(n <= 10){ 8: printf("%2d * %2d = %2d\n", num, n, num*n); 9: n = n + 1; 10: } /* moving on to the next multiplication table */ 11: num = num + 1; 12: } 13: return 0; 14: }
Problem: Change the previous program so that an empty line is placed after each multiplication table.
prog0406.c1: #include <stdio.h> 2: 3: int main(){ 4: int n, num; 5: while(num <= 5){ 6: n = 1; 7: while(n <= 10){ 8: printf("%2d * %2d = %2d\n", num, n, num*n); 9: n = n + 1; 10: } /* moving on to the next multiplication table */ 11: num = num + 1; 12: putchar('\n'); 13: } 14: return 0; 15: }
Note that the line change we want to do has to be done after the internal cycle has terminated (the cycle that shows an entire multiplication table). We use the function putchar, which receives a single character (between apostrophes) and places it on screen.
for
The for instruction (or for cycle, as its vulgarly known), adapts itself particularly to situations where the number of iterations is known a priori. Its syntax is:
for(init_charge; condition; post_instr) instruction;
Although it has a weird format, its a particularly well designed cycle, which summarizes, in a repetitive instruction, everything it needs.
Its functioning can be summarized by the following scheme:
- The code present in init_charge (initial charges) is executed. Normally, the variabled present in the cycle are initiated here. This component of the for cycle is executed only once.
- The condition is analyzed.
- If the condition's analysis returns False (zero), then the for cycle ends and the program continues on the immediately following instruction.
- If the condition's analysis returns True, then the cycle's instruction (or block of instructions) is executed.
- Back to square 2.
Let's check the program prog0401.c again:
prog0401.c1: #include <stdio.h> 2: 3: int main(){ 4: int i = 1; 5: while(i <= 10){ 6: printf("%d\n", i); 7: i = i+1; 8: } 9: return 0; 10: }
Since we know a priori how many times we need to iterate the cycle (10 times), let's rewrite the program using a for cycle.
Question: What are the necessary components for the cycle?
- We need to initiate the cycle's control variable.
- The condition is the same.
- We need to write the number on screen.
- In order to move from one iteration to the next, we need to increment the cycle's control variable one unit.
That way, the code
int i = 1; while(i <= 10){ printf("%d\n", i); i = i+1; }
can be written like this:
for(i=1; i <= 10; i = i + 1) printf("%d\n", i);
As you can see, the for cycle separates and identifies all of the components of a cycle:
- The initial charges.
- The condition that needs to be verified so the instruction is executed.
- The instruction.
- The jump being made from one instruction to the next.
Problem: Write a program that calculates the sum and the product of the n first natural numbers.
prog0407.c1: #include <stdio.h> 2: 3: int main(){ 4: int n, num, sum, product; 5: printf("Input a number: "); scanf("%d", &num); 6: 7: for(sum = 0, n = product = 1; n <= num; n = n + 1){ 8: sum = sum + n; 9: product = product * n; 10: } 11: printf("Sum = %d\nProduct = %d\n", sum, product); 12: return 0; 13: }
$ ./prog0407
Input a number: 5
Sum = 15
Product = 120
$
Problem: Write a program that prints the first 5 multiplication tables on-screen, stopping the screen after each of them are written.
prog0408.c1: #include <stdio.h> 2: 3: int main(){ 4: int i, j; 5: 6: for(i = 1; i <= 5; i = 1 + 1){ 7: for(j = 1; j <= 10; j = j + 1) 8: printf("%2d * %2d = %2d\n", i, j, i * j); 9: if(i != 5){ /* not to stop in the last iteration */ 10: printf("Press <ENTER> to continue..."); 11: getchar(); 12: } 13: } 14: return 0; 15: }
As you can see, the cycles are much smaller in size and much easier to read.
The external cycle allows you to peruse through the multiplication table values to print. The internal cycle allows you to write the current multiplication table.
After a multiplication table is completely printed out, the test is executed to check if the screen has to stop or not.
Since we want to stop the screen after each multiplication table, once each internal cycle ends, we write the message to press the <ENTER> key on-screen and, afterwards, we read a character using the getchar(). The read character isn's stored anywhere, since the value it gets isn't returned to any variable. It's content doesn't matter to us, so it's simply discarded.
Since it doesn't make any sense to stop the screen after writing the last multiplication table, we realize the test to verify if we are or not writing the last table, to make the decision of waiting or not the <ENTER> key.
Unlike most languages, in which the for cycle is a specific cycle to iterate a well determined number of times, in C the for cycle is nothing more than a while cycle.
That way, any for cycle
for(init_charge; condition; post_instr) instruction;
can always be rewritten as a while cycle.
init_charges; while(condition){ instruction; post_instr; }
do ... while
The do ... while clause (also known as the do ... while cycle) is different from the previous cycles because the condition test is executed AFTER executing the instruction (or instruction block) associated to the cycle, and not BEFORE, like in the while and for cycles.
That way, the body of the do ... while cycle is executed at least once, while in the while and for cycles, the cycle body is never executed (should the condition be False a priori).
The do ... while cycle's syntax is:
do instruction; while(condition);
The way it works can be described like this:
- The instruction (or block of instructions) is executed.
- The condition is analyzed.
- If the condition analysis returns True, we're back to square 1.
- If the condition analysis returns False, the cycle ends and the program continues in the instruction right after the cycle.
The do ... while cycle is particularly adapted to menu processing.
Problem: Write a program that presents a menu with the options Clients, Providers, Orders and Exit.
The program should present the option chosen by the user until he wants to exit.
prog0409.c1: #include <stdio.h> 2: 3: int main(){ 4: char option; 5: do { 6: printf("\tM A I N M E N U\n"); 7: printf("\n\n\t\tClients"); 8: printf("\n\n\t\tProviders"); 9: printf("\n\n\t\tOrders"); 10: printf("\n\n\t\tExit"); 11: printf("\n\n\n\t\t\tOption: "); 12: scanf("%c", &option); fflush(stdin); /* This cleans the keyboard buffer */ 13: switch(option){ 14: case 'c' : 15: case 'C' : puts("Option CLIENTS"); break; 16: case 'p' : 17: case 'P' : puts("Option PROVIDERS"); break; 18: case 'o' : 19: case 'O' : puts("Option ORDERS"); break; 20: case 's' : 21: case 'S' : break; /* Does nothing */ 22: default : puts("Option IS INVALID"); 23: } getchar(); /* Stop the screen */ 24: } while(option != 's' && option != 'S'); 25: return 0; 26: }
As you can see, the exit condition test is made only after presenting the menu at least once.
On line 12: fflush(stdin)
allows you to clean all existent characters in the keyboard buffer. More information on this function can obtained when I talk about files.
Cycles (In Short)
while | for | do ... while | |
---|---|---|---|
Syntax | while(cond) instruction; | for(init_ch; cond; pos-inst) instruction; | do instruction; while(condition); |
Executes the instruction | 0 or more | 0 or more | 1 or more |
Tests the condition | before the instruction | before the instruction | after the instruction |
Usage | common | common | uncommon |
Problem: Write a program that shows the 10 first even numbers.
prog0410.c1: #include <stdio.h> 2: 3: int main(){ 4: int i; 5: for(i = 1; i <= 10; i = i + 1) 6: printf("%2d\n", 2 * i); 7: return 0; 8: }
Note that the first 10 even numbers correspond to the first 10 integers multiplied by 2.
This program could have been written in another way.
prog0410.c1: #include <stdio.h> 2: 3: int main(){ 4: int i; 5: for(i = 2; i <= 20; i = i + 2) 6: printf("%2d\n", i); 7: return 0; 8: }
break
We already know the break instruction. Its previous role consisted in ending the set of instructions executed inside of a switch.
The break instruction, when applied inside of a cycle, ends the corresponding cycle, continuing the program on the instruction immediately after that cycle.
Question: What's the output of the next program?
prog0411.c1: #include <stdio.h> 2: 3: int main(){ 4: int i; 5: for(i = 1; i <= 100; i = i + 1) 6: if(i == 30) 7: break; 8: else 9: printf("%2d\n", 2 * i); 10: printf("CYCLE ENDS HERE\n"); 11: return 0; 12: }
Answer: It will show the first 29 even numbers (2, 4, ..., 68). When the variable i
has the value 30, the break instruction is executed, ending the cycle. However, the program continues on the next instruction, printing out the string "CYCLE ENDS HERE".
$ ./prog0411
2
4
6
...
56
58
CYCLE ENDS HERE
continue
The continue inside of a cycle allows you to skip the current instruction execution of the cycle body and skip to the next iteration.
Question: What's the output of the next program?
prog0412.c1: #include <stdio.h> 2: 3: int main(){ 4: int i; 5: for(i = 1; i <= 100; i = i + 1) 6: if(i == 60) 7: break; 8: else 9: if(i % 2 == 1) /* if i is an odd number */ 10: continue; 11: else 12: printf("%2d\n", i) 13: printf("CYCLE ENDS HERE\n"); 14: return 0; 15: }
Answer: The program will go through all numbers between 1 and 60 (because of the break). Every iteration checks if the number in question is an odd number. If it is, it skips to the next iteration of the cycle, otherwise, it prints out that number.
In short, it shows only the first 29 even numbers.
Chained Cycles
Chained cycles are cycles (while, for or do...while) that are present within other cycles.
There are no limitations as to how many cycles you can put inside other cycles.
Example (Bad implementation of chained cycles)
while(x <= 5){ /* while cycle starts here */ do{ /* do...while cycle starts here */ printf("Insert a number: "); } /* while cycle ends here */ scanf("%d", &n); } while(n < 0); /* do...while cycle ends here */
As you can see, the internal cycle is half in, half out of the external cycle. This situation is not allowed in C. The correct example would be:
while(x <= 5){ /* while cycle starts here */ do { /* do...while cycle starts here */ printf("Insert a number: "); scanf("%d", &n); } while(n < 0); /* do...while cycle ends here */ } /* while cycle ends here */
Problem: Write a program that places the following numbers on-screen:
1 1 2 1 2 3 ... 1 2 3 4 5 6 7 8 9 10
prog0413.c
1: #include <stdio.h> 2: 3: int main(){ 4: int i, j; 5: for(i = 1; i <= 10; i = i + 1){ 6: for(j = 1; j <= 1; j = j + 1) 7: printf("%d ", j); 8: putchar('\n'); 9: } 10: return 0; 11: }
This example had as a goal printing 10 lines on-screen. The nth line would only present the numbers 1 2 ... n. In that case, the internal cycle does not present a fix number of iterations, but it all depends on the external cycle's control variable value.
When you're in the presence of chained cycles, the passage of one iteration to another in the external cycle is only done after you complete the instruction block that composes it. Like that, the internal cycle works like a simple instruction, which has to be completely executed before skipping into the next iteration of the external cycle.
The previous example could have been written like this:
prog0414.c1: #include <stdio.h> 2: 3: int main(){ 4: int i, j; 5: for(i = 1; i <= 10; i = i + 1){ 6: for(j = 1; j <= 10; j = j + 1){ 7: printf("%d ", j); 8: if(j == i) 9: break; /* after writing j */ 10: } 11: putchar('\n'); 12: } 13: return 0; 14: }
Infinite Cycles
We call infinite cycles to cycles that never end, i.e., have conditions that are always true.
Examples:
while(1) instruction;
for( ; ; ) instruction;
do instruction; while(1);
These kinds of cycles are used normally when you don't know a priori how many times you're going to iterate the cycle. To end an infinite cycle, you use the break or return instructions.
Some coders use these kinds of cycles to process their menus.
while(1){ /* Present the menu */ /* Let's read the option */ if(option == ...) .... if(option == EXIT) break; /* exit the infinite cycle */ }
++ and -- Operators
C possesses a set of particularly useful operators, that allow you to increment or decrement variables, reducing significantly the quantity of written code.
As we've seen, in almost all presented programs, there are lines of code that repeat themselves in almost all programs and that are responsible for incrementing or decrementing variables.
prog0401.c1: #include <stdio.h> 2: 3: int main(){ 4: int i = 1; 5: while(i <= 10){ 6: printf("%d\n", i); 7: i = i+1; 8: } 9: return 0; 10: }
The C language has two unary operators that allow you to increment and decrement variables (cannot be used in constants).
Operator | Meaning | Examples |
---|---|---|
++ | Increment by 1 | i++ , ++k |
-- | Decrement by 1 | j-- , --alpha |
As you can see from the examples, both operators can be used either on the left or the right of the operands (variables).
Operator | Example | Equivalent |
---|---|---|
++ | x++ or ++x | x = x + 1 |
-- | x-- or --x | x = x - 1 |
So, the program prog0401.c could have been altered, replacing the line i = i + 1
by i++
or ++i
:
1: #include <stdio.h> 2: 3: int main(){ 4: int i = 1; 5: while(i <= 10){ 6: printf("%d\n", i); 7: i++; 8: } 9: return 0; 10: }
The unary operators ++ and -- can be used in expressions or even in more complex instructions than a simple variable increment or decrement. In these circumstances, the usage of operators at a variable's left or right could alter the final result.
The Difference between ++x and x++
When you execute:
y = x++; | y = ++x; |
---|---|
Two things happen, in this order: 1. The value of x is assigned to y 2. The value of x is incremented |
Two things happen, in this order: 1. The value of x is incremented 2. The value of x is assigned to y |
x = 5; y = x++; | x = 5; y = ++x; |
---|---|
Places the value 5 in the variable y. Next, it increments the variable x. |
The value of x is incremented. Next, the value of x is assigned to the variable y. |
Final values: x -> 6 and y -> 5 | Final values: x -> 6 and y -> 6 |
As you can see, the final value of the variables is not the same.
That way, the following equivalences are verified:
y = x++; | is equivalent to | y = x; x++; |
y = ++x; | is equivalent to | x++; y = x; |
What was previously presented for the ++ operator applies itself in the same way for the -- operator.
Question: what is the output for the following program?
int a = b = 3; printf(" a = %d and b = %d\n", a--, --b); printf(" a = %d and b = %d\n", a, b);
Notice that both of the variables are initiated with the value 3.
The first printf outputs the value of variable a (3) and, next, decrements it.
Next, it decrements variable b, and only then does it output it to the screen (2).
The second printf outputs the values present in a and b.
Therefore, the output would be
3 2 2 2
As can be verified, both variables are decremented one unit, but at different moments. The a variable is decremented after being used, while the b variable is decremented before being used.
Thus, another way the program prog0401.c could have been written goes as follows:
prog0401.c1: #include <stdio.h> 2: 3: int main(){ 4: int i = 1; 5: while(i <= 10) 6: printf("%d\n", i++); 7: return 0; 8: }
The i variable is printed out with printf. Immediately after being printed, it is incremented to next value, avoiding having to write the increment out of the printf, with the corresponding instruction block. Notice that the variable is incremented immediately after its usage by printf. That way, when the while's condition is tested again, the variable will already have its new value.
What's the output of the following code?
int i = 4; printf("%d", i++ + ++i);
Try it out to verify if the result was what you expected.
Example:
int i = 0; if(i++) /* After the condition test, the i variable is */ /* incremented, be it True or False */ ifs_instruction; /* Does not execute because the condition is False */ printf("%d", i); /* Prints the value 1 */
Complex Attribution
C allows us to reduce the quantity of written code every time we want a variable to get a value that depends on its own value.
Example:
x = x + 1; y = y * (a + 5 + b); z = z % 3;
In these situations, its unnecessary to repeat the variable name on the right side of our attribution. Let's suppose we wanted to add 3 to the variable x. Normally, we would write:
x = x + 3;
However, using a complex attribution, we can just write:
x += 3;
More generically, you can say that, being op a binary operator,
is equivalent to
Example | Meaning |
---|---|
x += 1 | x = x + 1 |
y *= 2 + 3 | y = y * (2 + 3) |
a -= b + 1 | a = a - (b + 1) |
k /= 12 | k = k / 12 |
r %= 2 | r = r % 2 |
This attribution writing is a little less readable than normal attribution. However, it's particularly useful when the variable name is big or particularly complex.
Conclusion
So now, you know to make and implement cycles in your program. Very nice.
But sometimes, writing a lot of code inside your main function can be very boring, and sometimes there's code we want to repeat in several sections of our program, and we want to invoke them instead of copy-paste them.
Check the next chapter to learn how to make your own functions and subroutines. ;)