Microsoft PowerPoint – 6_C_Control_Structures
O
SU
C
SE
2
42
1
J.E.Jones
Required Reading:
Pointers on C, Chapter 4 (skim is likely good enough), Section 5.1.9
O
SU
C
SE
2
42
1
J. E. Jones
Loop constructs in C
◦ for loops
◦ while loops
◦ do while loops
◦ break and continue
Conditional statements in C
◦ if
◦ switch-case
Boolean issues
The comma operator
Enumerated data types
O
SU
C
SE
2
42
1
J. E. Jones
Most of what we’ll cover today is consistent with what
you’ve seen in Java/C++
We’ll talk about things you CAN DO in ANSI C; it
doesn’t mean that you SHOULD DO them.
I’m sure most of you have heard the term “defensive
driving”. I’m going to spend time during the rest of
the semester talking about “defensive programming”
for equivalent reasons.
O
SU
C
SE
2
42
1
J. E. Jones
for (expression1; expression2; expression3)
{ statement(s); }
while (expression)
{statement(s); }
do {
statement(s);
} while (expression);
Statement(s) only execute when expression (expression2 in
for loop) is non-zero (TRUE).
Notice the semi-colon locations.
do while {statement(s)} always execute at least once (not
necessarily true for the other two constructs).
O
SU
C
SE
2
42
1
J. E. Jones
for (expression1; expression2; expression3)
{ statement(s); }
expression1 is the initialization and is evaluated only once
before expression2 is evaluated the first time.
expression2 is the condition and is evaluated once before
each execution of the body ({statement(s)}); if it evaluates
to a non-zero value, the body is executed; otherwise,
execution of the for-loop terminates, and execution
continues with the statement following the for-loop.
expression3 is called the adjustment and is evaluated after
all statements in the body are executed, and just before the
condition is evaluated again.
O
SU
C
SE
2
42
1
J. E. Jones
#include
int main () {
int n, sum = 0; /*declares n and sum; initializes sum*/
/* n=n+1 could be n++ or n+=1 or ++n */
for (n = 1; n <= 10; n = n + 1)
{
sum = sum + n;
}
printf("The sum of integers from 1 to 10 is %d\n", sum);
return 0;
}
O
SU
C
SE
2
42
1
J. E. Jones
while (expression)
{statement(s) }
expression is evaluated; if it evaluates to non-zero
(TRUE), {statement(s)} is/are executed, and expression is
evaluated again…
If expression evaluates to zero (FALSE), execution of the
while loop terminates, and the statement following the loop
is executed (the next statement after the }).
O
SU
C
SE
2
42
1
J. E. Jones
#include
int main () {
int n = 1, sum = 0;
while (n <= 10) {
sum = sum + n; /* could use sum += n instead */
n = n + 1; /* could use ++n instead */
}
printf("\n The sum of integers from 1 to 10 is %d\n",
sum);
return 0;
}
O
SU
C
SE
2
42
1
J. E. Jones
#include
int main()
{ int ch;
printf(“Please enter characters one at a time, followed by enter. When
you want to stop, enter the # character followed by enter:\n”);
while ((ch = getchar()) != ‘#’)
{
putchar(ch);
}
return(0);
}
/*What does this simple program do? */
/* what happens if we enter ‘\n’, but don’t put a ‘#’ before? */
O
SU
C
SE
2
42
1
J. E. Jones
do {
statement(s);
} while (expression);
statement(s) are executed once, and then expression is
evaluated; if it evaluates to non-zero (TRUE)
{statement(s)} are executed again, until expression
evaluates to zero (FALSE).
The result is that {statement(s)} will always be
executed at least once, even if expression is false before
the first execution.
O
SU
C
SE
2
42
1
J. E. Jones
#include
int main()
{ int print_it = 0;
do {
printf(“Hello World!\n”);
} while (print_it != 0);
return(0);
}
/* Hello World! is printed once, even though print_it is
zero before the loop body is executed. */
O
SU
C
SE
2
42
1
J. E. Jones
If a loop statement is only going to contain one line you can leave off
the enclosing {}
while(x < 10) printf(“%d”, x++); for(int i = 0; i < 10; ++i) printf(“%d”, i); Nonetheless, best practice is to use {} for clarity (or in case you need to add additional statements to the body of the loop later), but this shorthand is acceptable. O SU C SE 2 42 1 J. E. Jones Keywords that can be very important to looping are break and continue. ◦ break is valid in a loop or a switch construct ◦ continue is only valid in a loop construct although you can have a loop construct inside a switch construct break exits the innermost loop or switch statement (see below) which encloses it regardless of loop conditions continue causes the loop to stop its current iteration (do the adjustment in the case of for loops) and begin to execute again from the top. O SU C SE 2 42 1 J. E. Jones /* playing checkers */ while (1) { take_turn(player1); if((has_won(player1) || (wants_to_quit(player1) == TRUE)){ break; } take_turn(player2); if((has_won(player2) || (wants_to_quit(player2) == TRUE)){ break; } } O SU C SE 2 42 1 J. E. Jones /* playing monopoly */ for (player = 1; has_won() == 0; player++) { if (player > total_number_of_players) {
player = 1;
}
if (is_bankrupt(player)) {
continue;
}
take_turn(player);
}
O
SU
C
SE
2
42
1
J. E. Jones
goto plus a labeled statement
◦ goto identifier; /* identifier is called a label here */
◦ identifier: statement;
Don’t have to pre-define or declare the identifier
Identifiers must be unique
A statement label is meaningful only to a goto statement, otherwise it’s ignored
Both the keyword goto, and the label, must appear in a single function (i.e., you
can’t jump to a statement in a different function)
Identifier labels have their own name space so the names do not interfere with
other identifiers (however, for clarity, identifiers that are the same as variable or
function names should not be used!).
Best practice to use break, continue, and return statement in preference to goto
whenever possible (i.e., virtually always).
◦ Special case where goto is considered acceptable: exiting multiple levels of
loops (a deeply nested loop).
O
SU
C
SE
2
42
1
J. E. Jones
#include
int main() {
int i, j;
for ( i = 0; i < 10; i++ )
{
printf("Outer loop. i = %d\n",i);
for ( j = 0; j < 3; j++ ) {
printf("Inner loop. j = %d\n",j);
if ( i == 1 ) goto stop;
}
}
/* This message does not print: */
printf("Loop exited. i = %d\n", i );
stop:
printf( "Jumped to stop. i = %d\n", i );
return (0);
}
O
SU
C
SE
2
42
1
J. E. Jones
#include
int main() {
int i, j;
for ( i = 0; i < 10; i++ )
{
printf("Outer loop. i = %d\n",i);
for ( j = 0; j < 3; j++ ) {
printf("Inner loop. j = %d\n",j);
if ( i == 1 ) goto stop;
}
}
/* This message does not print: */
printf("Loop exited. i = %d\n", i );
stop:
printf( "Jumped to stop. i = %d\n", i );
return (0);
}
Output:
Outer loop. i = 0
Inner loop. j = 0
Inner loop. j = 1
Inner loop, j = 2
Outer loop. i = 1
Inner loop, j = 0
Jumped to stop. i = 1
O
SU
C
SE
2
42
1
J. E. Jones
The switch statement is similar to a
chain of if/else statements, but
typically it executes significantly
faster.
The statement evaluates the
expression (usually a variable) and
then looks for a matching case label
and jumps there; case labels must be
unique, constant values. If not unique,
it picks the first one. If no match is
found, the default label is used.
A break statement inside a switch
says, “continue execution after the
switch”. If it’s not there, execution
goes to the next statement (which
could be the 1st statement in the next
case). This is called “fall through”.
General form is:
switch(expression) {
case constant1:
statement;
. . . . .
break;
case constant2:
statement;
. . . . .
/* fall through */
case constant3:
statement;
. . . . .
break;
default:
statement;
. . . . .
break;
}
O
SU
C
SE
2
42
1
J. E. Jones
Syntax for if and else:
if (operator = = '+') {
result + = value;
}
else if (operator = = '-') {
result-= value;
}
else if (operator = = '*') {
result *= value;
}
else if (operator = = '/') {
if (value = = 0) {
printf(" Error:Divide by zero\ n");
printf(" operation ignored\ n");
}
else result /= value;
}
else {
printf(" Unknown operator %c\ n", operator);
}
Syntax for switch:
switch (operator) {
case '+':
result + = value;
break;
case '-':
result-= value;
break;
case '*':
result *= value;
break;
case '/':
if (value = = 0) {
printf(" Error:Divide by zero\ n");
printf(" operation ignored\ n");
}
else result /= value;
break;
default:
printf(" Unknown operator %c\ n",
operator);
break;
}
O
SU
C
SE
2
42
1
J. E. Jones
if (expression1) {
statement(s); }
else if (expression2) {
statement(s); }
else {
statement(s); }
if ( expression ) {
/* Execute these stmts if
expression != 0 */ }
else {
/* Execute these stmts if
expression == 0 */ }
switch ( expression ) {
case value1: /* Note - use : not ; */
Code to execute if
break;
case value2:
Code to execute if
break;
…
default:
Code to execute if
following any of the cases…
break;
}
• SWITCH NOTES:
• Notice, no {} blocks within each case!
• Notice the colon for each case and value.
• value1, value2, etc. in a switch statement must be constant
values; expression should evaluate to some integer type
• The default case is optional, but it is wise to include it as it
handles any unexpected cases.
• Fall-through is allowed (if break is omitted)
• Chooses first value that matches…
O
SU
C
SE
2
42
1
J. E. Jones
/* Suppose this code, with scores from 0 to 100 */
int score = 80;
if (score >= 60)
if (score >= 90)
printf(“Congratulations, you got an A!\n”);
else printf(“Unfortunately, that’s not passing!\n”);
Output?
O
SU
C
SE
2
42
1
J. E. Jones
Output:
Unfortunately, that’s not passing!
What happened? We only wanted this message for scores
< 60.
O
SU
C
SE
2
42
1
J. E. Jones
int score = 80;
if (score >= 60)
if (score >= 90)
printf(“Congratulations, you got an A!\n”);
else printf(“Unfortunately, that’s not passing!\n”);
The problem is that the compiler does not pay attention to
formatting, so even though the else is aligned (format-wise)
with the first if, the compiler pairs it with the second if.
Rule: The compiler pairs any else with the closest (most
recent) unmatched if which is not enclosed in a different
block.
“Unmatched” here means that the if has not already been
paired with an else.
O
SU
C
SE
2
42
1
J. E. Jones
int score = 80;
if (score >= 60) {
if (score >= 90)
printf(“Congratulations, you got an A!\n”);
}
else printf(“Unfortunately, that’s not passing!\n”);
Now, the else will be paired with the first if, because the second one is
enclosed in a different block.
What prints when score = 80 with the code above?
O
SU
C
SE
2
42
1
J. E. Jones
#include
int main() {
int age;
printf( “Please enter your age” );
scanf( “%d”, &age );
if ( age < 100 ) {
printf ("You are young!\n" );
}
else if ( age == 100 ) {
printf("You are old.\n" );
}
else {
printf("You are really old!\n" );
}
return 0;
}
O
SU
C
SE
2
42
1
J. E. Jones
switch ( x ) {
case 'a':
/* Do stuff when x == 'a' */
break;
case 'b’:
/* code we use if x==‘b’
but not ‘c’ or ‘d’, then
fall through */
case 'c':
case 'd':
/* Fall-through technique...
cases ‘b’,’c’,’d’ all use this code */
break;
default:
/* Handle cases when x is not ‘a’, ’b’, ’c’ or ‘d’.
ALWAYS have a default case even though it’s not
required */
break; /* this break is not necessary, but is legal
and creates “completeness” */
}
O
SU
C
SE
2
42
1
J. E. Jones
Every Boolean test is an implicit comparison against zero (0).
However, zero is not a simple concept. It represents:
1. the integer 0 for all integer types
2. the floating point 0.0 (positive or negative)
3. the null character (‘\0’)
4. the null pointer (NULL) -- #define NULL 0
In order to make your intentions clear, explicitly show the
comparison with zero for all scalars, floating-point numbers,
and characters.
O
SU
C
SE
2
42
1
J. E. Jones
Every Boolean test is an implicit comparison against zero (0).
However, zero is not a simple concept. It represents:
1. the integer 0 for all integer types
2. the floating point 0.0 (positive or negative)
3. the null character (‘\0’)
4. the null pointer (NULL) -- #define NULL 0
In order to make your intentions clear, explicitly show the
comparison with zero for all scalars, floating-point numbers,
and characters.
This becomes more significant (and needful to understand
as a concept) when we get to x86-64
O
SU
C
SE
2
42
1
J. E. Jones
int i; if (i) is better represented as if (i != 0)
float x; if (!x) is better represented as if (x == 0.0)
char c; if (c) is better represented as if (c != '\0’)
int* ptr; if(*ptr) better as if(*ptr !=NULL)
An exception is made for pointers, since 0 is the only
language-level representation for the NULL pointer.
The symbol NULL is not part of the core language - you
must include a special header file to get it defined (stdlib.h
or stddef.h). More on this later.
O
SU
C
SE
2
42
1
J. E. Jones
To write an INFINITE LOOP
◦ for (;;) ...
◦ while (1) ...
The former is more visually distinctive, but both forms
are used.
In industry code that I saw, while(1) was used almost
exclusively
O
SU
C
SE
2
42
1
J. E. Jones
Used to link related expressions together
Evaluated from left to right
The value of the right-most expression is the value of the whole
combined expression
Parentheses are necessary; if no parentheses surround an expression
with commas, the commas are separators, and not the comma operator!
Example:
◦ i = a, b, c; /*stores a into i – commas here are separators*/
◦ i = (a, b, c); /*stores c into i – these commas are comma operators*/
For loop:
◦ for (n=1, m=10; n<=m; n++, m--)
While:
◦ while (c=getchar(), c!= ‘0’)
Exchanging values:
◦ (t=x, x=y, y=t); /*Better not to use comma operator unless you want to use this
as a boolean, for example, to control a loop or an if or if-else conditional*/
O
SU
C
SE
2
42
1
J. E. Jones
Consider:
#include
int main(){
int a = (10, 5);
int b = 1;
int c = 10;
int d;
if (d = (b = a, a = ++b, c = 0)) {
printf(“Writing a test program is sometimes the only way to be sure.\n”);
}
printf(“a=%d, b=%d, c=%d, d=%d\n”, a, b, c, d);
}
Output:
◦ [jones.5684@cse-fl1 test]$ comma
◦ a = 6, b = 6, c = 0, d = 0
◦ [jones.5684@cse-fl1 test]$
Why output is as shown is left as an exercise for the reader.
O
SU
C
SE
2
42
1
J. E. Jones
Consider:
#include
int main(){
int a = (10, 5);
int b = 1;
int c = 10;
int d;
if (d = (b = a, a = ++b, c = b)) {
printf(“Writing a test program is sometimes the only way to be sure.\n”);
}
printf(“a=%d, b=%d, c=%d, d=%d\n”, a, b, c, d);
}
Output:
◦ [jones.5684@cse-fl1 test]$ comma
◦ Writing a test program is sometimes the only way to be sure.
◦ a = 6, b = 6, c = 6, d = 6
◦ [jones.5684@cse-fl1 test]$
Why output is as shown is left as an exercise for the reader.
O
SU
C
SE
2
42
1
J.E.Jones
The C Language – Enumerated Data Types
O
SU
C
SE
2
42
1
J. E. Jones
An enumerated data type is designed for variables that
contain only a limited set of values.
◦ These values are referenced by name(tag).
◦ The compiler assigns each tag an integer value internally.
An enumerated type is one whose values are symbolic
constants rather than literals.
Declaration example:
◦ enum Container_Type {CUP, PINT, QUART,
HALF_GALLON, GALLON};
Declaration of a variable of the above type:
◦ enum Container_Type milk_bottle;
O
SU
C
SE
2
42
1
J. E. Jones
Variables declared with an enumerated type are actually stored as
integers.
Internally, the symbolic names are treated as integer constants, and it
is legal to assign them values, e.g.:
◦ enum Container_Type {CUP=8, PINT=16, QUART=32,
HALF_GALLON=64, GALLON=128};
◦ enum Container_Type milk_bottle;
◦ Otherwise, by default, CUP =0, PINT=1, QUART=2, etc.
milk_bottle=HALF_GALLON;
Caution: don’t mix them indiscriminately with integers – even
though it is syntactically valid to do so.
◦ milk_bottle = -623; /*A bad idea, and likely to lead to trouble*/
◦ int a = PINT; /*Also a bad idea, and likely to lead to trouble*/
O
SU
C
SE
2
42
1
J. E. Jones
If there is only one declaration of variables of a particular
enumerated type (i.e., no type name), both statements may
be combined:
◦ enum {CUP, PINT, QUART, HALF_GALLON, GALLON}
milk_bottle, gas_can, medicine_bottle;
milk_bottle, gas_can, and medicine_bottle are now all
instances of the enum type, and all these variables can be
assigned CUP, PINT, etc.
No more variables of this type can be declared later
because no type name was given to it
Nor can pointers to this variable type be declared
O
SU
C
SE
2
42
1
J. E. Jones
#include
main() {
enum month {jan = 1, feb=2, mar=3, apr=4, may=5,
jun = 6, jul=7, aug=8, sep=9, oct=10,
nov = 11,dec=12 } this_month;
this_month = feb;
printf(“month : %d\n”,this_month);
}
Output??
O
SU
C
SE
2
42
1
J. E. Jones
#include
main() {
enum month {jan = 1, feb=2, mar=3, apr=4, may=5,
jun = 6, jul=7, aug=8, sep=9, oct=10,
nov = 11,dec=12 } this_month;
this_month = feb;
printf(“month : %d\n”,this_month);
}
Output??
month: 2