37
CHAPTER 4 Flow of Control Constructs The goto statement · C has a goto statement · Its syntax is as follows goto_statement ::= goto label ; label ::= identifier · The label does not have to be declared, it simply must exist somewhere within that same function · The label is simply an identifier example for (...) for (...) { ... if (disaster) goto error; } ... error: clean up the mess · This approach is useful if the error handling code is non-trivial, or if errors can occur in many places · The alternative is to use flags to indicate whether we are in an error state and to then exit the loop, thus requiring an explicit test, when such an error state exists

sophiasapiens.chez.comsophiasapiens.chez.com/informatique/C-Programming/Ch04... · Web viewThe controlling expression of the switch statement must be integer valued. The usual automatic

Embed Size (px)

Citation preview

CHAPTER 4 Flow of Control Constructs

The goto statement

· C has a goto statement· Its syntax is as follows

goto_statement ::= goto label ;label ::= identifier

· The label does not have to be declared, it simply must exist somewhere within that same function

· The label is simply an identifier

example

for (...)for (...) {

...if (disaster)

goto error;}...

error:clean up the mess

· This approach is useful if the error handling code is non-trivial, or if errors can occur in many places

· The alternative is to use flags to indicate whether we are in an error state and to then exit the loop, thus requiring an explicit test, when such an error state exists

Labeled statements

· More formally, statements may be preceded with a label which can either be jumped to via a goto statement or technically within a switch statement

labeled_statement ::= identifier : statement| case constant-expression : statement| default : statement

switch_statement ::= switch (expression) statement

· The label names are said to have function scope, that is they must be unique within a given function.

· However, the same label may be used in different functions.· This makes sense considering that you cannot jump from one function to

another via a goto statement

· Goto statements can and should be avoided· In general, by breaking things up into small enough functions, one can

return from them at any point and emulate a goto· If you find the need to use a goto, then you should probably consider

making the code from which you branched a separate function; then return from it when you would have done a goto

The switch statement

Reference: Rojiani, Chapter 6 (6.11)

The following is a typical example of a switch statement:

switch (c) {case 'a':

++a_cnt;break;

case 'b':++b_cnt;break;

case 'c': /* FALLTHRU */case 'C':

++c_cnt;break;

case 'd':++d_cnt; /* note duplication */break;

case 'D':++d_cnt;break;

default:++other_cnt;break;

}

· The controlling expression of the switch statement must be integer valued. The usual automatic conversions are performed on the controlling expression.

· Each case is labeled by one or more integer-valued constant expressions.· Each case expression must be unique.· Default case is optional and is executed when no other cases are satisfied.· Cases and default clause may appear in any order.· The break statement, when encountered, causes an immediate exit from

the switch.· Without the break statement, flow of control falls through to the next

case. This allows several case labels to have a single action or share a particular action. Normally, each case must end with a break statement.

The switch statement (2) -- the effect of a switch

1. Evaluate the switch statement

2. Go to the case label having a constant value that matches the value of the expression found in step 1, or if a match is not found, go to the default label, or if there is no default label, terminate the switch.

3. Terminate the switch when a break statement is encountered, or terminate the switch by "falling off the end."

example

void printit (int i){

switch (i) {case 0:

printf ("I see a zero\n");case 1:

printf ("That was either a 0 or 1.\n");break;

case 2: /* FALLTHRU */ case 3:

printf ("Either a 2 or 3 -- %d\n", i);break;

case 4:printf ("I see a 4.\n");break;

case -1:printf ("A -1\n");

case -2:printf ("Either a -1 or -2\n");break;

default:printf ("A %d -- interesting\n", i);break; /* not required */

}}

Syntax of switch statement

· The precise syntax for a switch statement is as follows

Switch_statement ::= switch (integer-expression) statementLabeled_statement ::= identifier : statement

| case constant-integer-expression : statement| default : statement

statement ::= labeled-statement| expression ;| ;| compound-statement| selection-statement| iteration-statement| jump-statement

Compound-statement ::= { {declarator}* {statement}* }

· The switch statement is a multiway conditional statement· It generalizes upon the if-else construct· Informally, the syntax for a switch statement is as follows

switch_statement ::= switch (integer_valued_expression)labeled_statement | switch_block

case_statement ::= {case_part}+ statementcase_part ::= case constant_integer_expression :

default_statement ::= default : statementswitch_block ::= { {declaration_list}? {case_group}*

{default_group}? {case_group}* }case_group ::= {case_part}+ {statement}+

default_group ::= default : {statement}+

· Names which are in italics represent nonterminals.· If nonterminals are enclosed in {braces}, then they are either followed by

a + (meaning one or more), a * (meaning zero or more) or a ? (meaning zero or one, i.e. optional)

· Keywords and terminals are in boldface

The switch statement versus else-if constructs

· Many switch statements can be written as a series of if-else statements.· However, in many cases, the switch statement is actually more efficient

than the if-else.· In the following example, the if-else will perform each test in sequence

until it finds a match.· The switch statement, on the other hand, will evaluate c and then jump

directly to the desired statement.· Most C compilers create jump tables for the switch statement, thus

allowing the program to find the desired case instantaneously as opposed to searching for it one by one.

switch solution equivalent if-else solutionswitch (c) {case 'a': case 'A':

...break;

case 'b': case 'B':...break;

case 'c': case 'C':...break;

default:...break;

}

if (c == 'a' || c == 'A'){...

} else if (c == 'b' || c == 'B'){...

} else if (c == 'c' || c == 'C'){...

} else {...

}

Bitwise operators

Reference: Kelley & Pohl, Chapter 7

· C provides six operators for bit manipulation· These may be applied only to integral operands (signed or

unsigned), e.g. char, short, int, or long.· Operands should be unsigned for portability

Operator Semantics& bitwise and| bitwise or^ bitwise exclusive or<< left shift>> right shift~ one's complement negation (unary)

· An integer operand is treated as if it were a string of bits· Bitwise operators in C perform those operations on each corresponding

bit· These operations are illustrated in the truth table below

x y x & y x | y x ^ y ~ x0 0 0 0 0 10 1 0 1 1 11 0 0 1 1 01 1 1 1 0 0

Bitwise operators (2) -- example

Compute the following:

0x3762 & 0xABCD

Treat each hex digit as a 4-digit binary numberUsing this approach, each digit is an abbreviation for the following binary

0 = 0000 4 = 0100 8 = 1000 C = 11001 = 0001 5 = 0101 9 = 1001 D = 11012 = 0010 6 = 0110 A = 1010 E = 11103 = 0011 7 = 0111 B = 1011 F = 1111

Thus,

0x3762 = 0011 0111 0110 00100xABCD = 1010 1011 1100 1101

& ------------------------------- 0010 0011 0100 0000 = 0x2340

The bitwise operator acts on each individual corresponding bitSimiliary,

07142 | 05216

Treat each octal digit as a 3-digit binary number. Thus,

07142 = 111 001 100 01005216 = 101 010 001 110

| -------------------------- 111 011 101 110 = 07356

Bitwise operators (3) -- example

n = n & 0177;

· Unsigned integer constants are guaranteed to behave as if they have the naive binary representation, i.e.

· Constant 0177 contains 0...01111111· The example above uses the octal constant to mask off all but the 7 low

order bits· That is, n gets assigned an integer with its 7 low order bits and with all

other bits set to 0

0 & x = 01 & x = x

· This is portable· The compiler is required to generate the correct code· The compiler has the job of handling case of how bytes are arranged

within a word· It is the compiler's job to support the C model and to generate whatever

machine code is necessary to make it work

Bitwise operators (4) -- example

x = x | 03; /* binary 011 */

· In this example, the or '|' operator is used to turn the 2 low order bits on.· The other bits are left unchanged.

0 | x = x1 | x = 1

x = 0x7F ^ 0xA;

· In this example, the exclusive or '^' operator puts a 1 in each bit position where its operands have different bits, and a 0 where they have the same bits.

x <-- 0111 1111 ^ 0000 1010 0111 0101 0x75

NOTE

· Logical operators && and || are different from bitwise operators & and |

x = 1; y = 2;x & y /* zero */x && y /* one */

Bitwise operators (5) -- example

x = x & ~077

· In this example, the not '~' operator performs one's complement negation.· The effect of this statement is to set the 6 low-order bits of x to zero.· This is a machine independent operation, i.e.:

~077 == 111...1000000where the number of leading ones depends on the machine's word size.

example

On a 16 bit machine:~1 == 0xfffe

On a 32 bit machine:~1 == 0xfffffffe

Right and left shift

· Places zeroes in vacated bits of positive (unsigned) constants· Remember them by the way they point:

>> right shift<< left shift

expression binary value Result binary value0x4U << 2 0000 0100 0x10 0001 00000x8U >> 2 0000 1000 0x2 0000 0010

Bitwise operations (6) -- printing an int bitwise

· The following function will use masks to print out an unsigned integer argument bitwise.

/* print an unsigned int expression bitwise */void print_bits (unsigned int v){

int i;int N = 32; /* assume 32 bits */unsigned int mask = 1 << (N - 1); /* 100...0 */

for (i = 0; i < N; i++){

if ((v & mask) == 0)putchar ('0');

elseputchar ('1');

mask >>= 1; /* mask = mask >> 1 */if ((i % 8) == 7 && i != (N - 1))

putchar (' ');}

}

· Because we are printing out all the bits in an unsigned integer, this routine cannot be written without knowing how large an integer is on that machine.· Unfortunately, C has no operator to determine this· It is possible, however, to write a function to do this· Likewise, macros in <limits.h> can be used to determine this

· Variable mask is initialized so that it has a 1 in the high order bit andzeroes everywhere else, i.e. 000...001 << 31 yields the bit string 100...000

· The loop prints out each digit with one at a time, starting with the most significant digit

· After printing 8 digits, it prints a space· It is very careful not to print a space after the very last digit

Bitwise operators (7) -- figuring how many bits may be shifted

· In the previous exercise, we assumed that an unsigned integer had 32 bits which could be printed

· What we really want to be able to determine is how many bits we can get at by shifting

1234567891011121314

#include <stdio.h>

int main (void){

int i;unsigned int x = 0x1;

for (i = 0; x != 0; i++)x = x << 1; /* x <<= 1 */

printf ("%d bits in an unsigned int\n", i); return 0;}

· This approach was used to determine bit sizes on a 200 MHz Pentium/MMX (Windows NT 4.0 SP3, MSVC 5.0) and the Unisys A-Series (mva15a)

Type Bitsize on P200/NT Bitsize on A-SeriesChar 8 8

unsigned char 8 8short 16 39

unsigned short 16 39int 32 39

unsigned int 32 39long 32 39

unsigned long 32 39

Bitwise operators (8) -- example

Suppose we want to turn ON bit 15 in an unsigned long variable xWe do so by OR-ing in a 1 at the appropriate positionThe integer 1 has a 1 at position 0, i.e. its bit pattern is

000...001

We need to shift this so that the 1 is in bit position 15, not 0We can achieve this by shifting it to the left 15 positions, i.e.

1 << 15

Thus, to turn on bit 15 in x, we OR this expression, as in

x | 1 << 15

This expression is x with bit 15 turned onWe can modify x by incorporating an assignment

x |= 1 << 15;

Bitwise operators (9) -- example

Suppose we want to turn OFF bit 12 in an unsigned long variable xWe do so by AND-ing in a 0 at the appropriate positionAll of the other bits in the mask must be 1The integer 1 has a 1 at position 0, i.e. its bit pattern is

000...001

If we complement 1, as in ~1, then its bit pattern is

111...110

We need to first shift the 1 so that it is in bit position 12, not 0We can achieve this by shifting it to the left 12 positions, i.e.

1 << 12

We then complement this pattern:

~(1 << 12)

Thus, to turn off bit 12 in x, we AND this expression, as in

x & ~(1 << 12)

This expression is x with bit 12 turned off

Naturally, we can also modify x itself, as follows:

x &= ~(1 << 12)

Bitwise operators (10) -- example

Suppose we wish to extract 8 bits from x starting at position 15We notate this abstractly by writing x[15:8]First we need to create a mask with 8 bits on and AND it with xTo create the mask, we complement 0 and shift it left 8 positions:

~0 << 8

Note that the 8 low-order vacated bits are filled with 0's.We complement that again so that 8 vacated bits become 1's

~(~0 << 8)

We now shift the bits at position 15 right so that they are right justifiedThat us we want bit 15 in x to become bit 7To do this we right shift x 8 bits:

x >> 8

Now we can AND these two expressions together:

~(~0 << 8) & (x >> 8)

Bitwise operators (11) -- example

· Write a function to return an n-bit field from its argument x starting at position p, assuming bit 0 is rightmost.

· We notate this abstractly by writing x[p:n]

/** ** getbits: get n bits from position p */ **/unsigned intgetbits (unsigned int x, unsigned int p, unsigned int n){

unsigned int one, mask, right_x;

/* first create a string of ones */one = ~0;

/* put zeroes in the rightmost n bits, then negate to make it all ones as the rightmost n digits */

mask = ~(one << n);

/* p denotes the left-most bit desired; shiftargument so that the n bits starting at positionp are rightmost */

right_x = x >> (p + 1 – n);

/* return result */return right_x & mask;

}

Bitwise operators (12) -- precedence

· The following table lists the operators we have seen thus far· Reference: p. 53 of K&R

Operator Associativity() left to right

+ (unary) - (unary) !~ ++ -- ( type )

right to left

* / % left to right+ - left to right<< >> left to right

< <= > >= left to right== != left to right

& left to right^ left to right| left to right&& left to right|| left to right

· Notice that the left and right shift operators are higher in precedence than the relational operators

· On the other hand, the bitwise operators are lower in precedence than the comparison operators

· Also, notice that bitwise and, exclusive-or, and or are all different in precedence.

· The following example illustrates a potential bug one may encounter if one does not pay attention to precedence.

if (v & mask == 0)putchar ('0');

elseputchar ('1');

· The problem is that == has higher precedence than & and so the test is really v & (mask == 0) which is not what the user wanted.

Bitwise operators (13) -- two's complement

· Many machines (and not the Unisys A-Series) use two's complement to represent negative numbers.

· A one's complement bit string is obtained by doing a bitwise negation on the bit string, i.e. converting each corresponding 0 to 1 and each corresponding 1 to 0 (as the ~ operator does)

· A two's complement bit string is obtained by taking the one's complement and than adding 1 to it

· On a two's complement machine, the hardware that does an addition can also be used to do subtraction. The operation a-b is the same as a+(-b) where -b is obtained by taking the one's complement and then adding 1

· The following table illustrates what various numbers would be on an 8 bit machine

Value Binary 1's comp 2's comp Value0 00000000 11111111 00000000 01 00000001 11111110 11111111 -12 00000010 11111101 11111110 -23 00000011 11111100 11111101 -34 00000100 11111011 11111100 -4

· Notice that on a two's complement machine, the two's complement of 0 is still all zeroes

· The two's complement of 1 is all ones· Thus on a two's complement machine, 0 is all bits off and -1 is all bits on· Bitwise operations are portable only on unsigned numbers

Negative numbers -- avoid them when doing bitwise operations· Doing bitwise operations on negative numbers can yield unpredictable

results· A negative constant's binary representation is machine dependent· Shift operations fill vacated bits with zeroes on unsigned quantities;

however, on signed quantities, right shifting may fill with sign bits (arithmetic shift) or with 0-bits (logical shift) depending on the architecture

· Avoid using negative constants in bit operations· Thus, do not use signed variables for bitwise operations but use unsigned

variables instead

Precedence and order of evaluation - a complete table

Operator Associativity Order of Evaluation() [] -> . left to right -! ~ ++ --

(unary) + -(indirection) *& ( type )

sizeof

right to left -

* / % left to right -+ - left to right -

<< >> left to right -<= < > >= left to right -== != left to right -

& left to right -^ left to right -| left to right -

&& left to rightleft to right

sequence point after first argumentshort circuit

|| left to rightleft to right

sequence point after first argumentshort circuit

?: right to left first operand evalsequence point after

first argument=

+= -=*= /= %=

&= |= <<= >>=

right to left -

, left to rightleft to right

sequence point after first argument

The DO construct

· The do statement can be considered to be a variant of the while statement· It makes its test at the bottom of the loop instead of at the top· Its syntax is as follows

do_statement ::= do statement while (expression);

· The following table gives a do construct and its equivalent while construct

· Note that the body of the do construct is performed at least once

do construct equivalent while construct

do {scanf ("%d", &i);sum += i;

} while (i > 0);

scanf ("%d", &i);sum += i;while (i > 0) {

scanf ("%d", &i);sum += i;

}do

statementwhile (expression);next statement

Statementwhile (expression)

statementnext statement

Semantics of the DO construct· The statement is executed· Then the expression is evaluated

· If nonzero (true), the loop continues· If zero (false), the loop terminates

· Sort of like a Pascal repeat-until construct, but not really

C Pascaldo <something>while <expression> is true

repeat <something>until <expression> is true

· This can confuse people, especially Pascal programmers

The DO construct (2) -- example

i = sum = 0;do {

sum += i;scanf ("%d", &i);

} while (i > 0);

· This code fragment reads in digits until the user types in a negative or zero digit

· do/while look a lot like while statements -- style note· Always enclose statements inside braces { } even if you only have

one statement· That is, always make <statement> a compound statement so that the

do/while is not confused with the while construct· Thus, avoid code which looks like this:

void foo(int c){

/* Parse the current character and then read * in some more and parse those too... */do switch (c) { case 'a':

...break;

case 'b':...break;

case 'c':...break;

default:...break;

}while ((c = getchar ()) != EOF);

}

break and continue

· The following two statements have the a special effect within C

break; continue;exit from the innermost enclosing loop or switch statement

cause the current iteration of a loop to stop and cause the next iteration of the loop to begin immediately

· The continue statement is a bit tricky· The following table illustrates its effect on the various looping constructs

for passes control to the increment step (third expression)do passes control to the bottom (test) part of the loop

while passes control to the top (test) part of the loop

int c = 0;

while (c != EOF) {c = getchar ();if (c == 'y')

break;if (c == 'n')

continue;putchar (c);

}

· The loop stops when it sees a 'y'· All characters besides an 'n' are printed

sum = 0;for (i = 0; i < n; i++) {

if (i % 3 == 0) continue;

if (i % 5 == 0) continue;

sum += i;}

· This fragment adds up numbers not divisible by 3 or 5.