Showing posts with label operator. Show all posts
Showing posts with label operator. Show all posts

Tuesday, September 21, 2010

Selection Statements if and switch

C/C++ supports two types of selection statements: if and switch. In addition, the ?
operator is an alternative to if in certain circumstances.
if
The general form of the if statement is
if (expression) statement;
else statement;
where a statement may consist of a single statement, a block of statements, or nothing
(in the case of empty statements). The else clause is optional.
If expression evaluates to true (anything other than 0), the statement or block that
forms the target of if is executed; otherwise, the statement or block that is the target of
else will be executed, if it exists. Remember, only the code associated with if or the
code associated with else executes, never both.
In C, the conditional statement controlling if must produce a scalar result. A scalar
is either an integer, character, pointer, or floating-point type. In C++, it may also be of
type bool. It is rare to use a floating-point number to control a conditional statement
because this slows execution time considerably. (It takes several instructions to perform
a floating-point operation. It takes relatively few instructions to perform an integer or
character operation.)
The following program contains an example of if. The program plays a very simple
version of the "guess the magic number" game. It prints the message ** Right ** when
the player guesses the magic number. It generates the magic number using the
standard random number generator rand() , which returns an arbitrary number
between 0 and RAND_MAX (which defines an integer value that is 32,767 or larger).
rand() requires the header file stdlib.h. (A C++ program may also use the new-style
header <cstdlib>.)
/* Magic number program #1. */
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int magic; /* magic number */
int guess; /* user's guess */
magic = rand(); /* generate the magic number */
printf("Guess the magic number: ");
scanf("%d", &guess);
if(guess == magic) printf("** Right **");
return 0;
}
Taking the magic number program further, the next version illustrates the use of
the else statement to print a message in response to the wrong number.
/* Magic number program #2. */
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int magic; /* magic number */
int guess; /* user's guess */
magic = rand(); /* generate the magic number */
printf("Guess the magic number: ");
scanf("%d", &guess);
if(guess == magic) printf("** Right **");
else printf("Wrong");
return 0;
}

Nested ifs
A nested if is an if that is the target of another if or else. Nested ifs are very common
in programming. In a nested if, an else statement always refers to the nearest if
statement that is within the same block as the else and that is not already associated
with an else. For example,

if(i)
{
if(j) statement 1;
if(k) statement 2; /* this if */
else statement 3; /* is associated with this else */
}
else statement 4; /* associated with if(i) */
As noted, the final else is not associated with if(j) because it is not in the same block.
Rather, the final else is associated with if(i). Also, the inner else is associated with if(k),
which is the nearest if.
Standard C specifies that at least 15 levels of nesting must be supported. In practice,
most compilers allow substantially more. More importantly, Standard C++ suggests
that at least 256 levels of nested ifs be allowed in a C++ program. However, nesting
beyond a few levels is seldom necessary, and excessive nesting can quickly confuse the
meaning of an algorithm.
You can use a nested if to further improve the magic number program by
providing the player with feedback about a wrong guess.
/* Magic number program #3. */
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int magic; /* magic number */
int guess; /* user's guess */
magic = rand(); /* get a random number */
printf("Guess the magic number: ");
scanf("%d", &guess);
if (guess == magic) {
printf("** Right **");
printf(" %d is the magic number\n", magic);
}
else {
printf("Wrong, ");
if(guess > magic) printf("too high\n");
else printf("too low\n");
}
return 0;
}
The if-else-if Ladder
A common programming construct is the if-else-if ladder, sometimes called the if-else-if
staircase because of its appearance. Its general form is
if (expression) statement;
else
if (expression) statement;
else
if (expression) statement;
..
.
else statement;
The conditions are evaluated from the top downward. As soon as a true condition is
found, the statement associated with it is executed and the rest of the ladder is
bypassed. If none of the conditions are true, the final else is executed. That is, if all
other conditional tests fail, the last else statement is performed. If the final else is not
present, no action takes place if all other conditions are false.
Although the indentation of the preceding if-else-if ladder is technically correct, it
can lead to overly deep indentation. For this reason, the if-else-if ladder is generally
indented like this:
if (expression)
statement;
else if (expression)
statement;
else if (expression)
statement;
.
..
else
statement;
Using an if-else-if ladder, the magic number program becomes
/* Magic number program #4. */
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int magic; /* magic number */
int guess; /* user's guess */
magic = rand(); /* generate the magic number */
printf("Guess the magic number: ");
scanf("%d", &guess);
if(guess == magic) {
printf("** Right ** ");
printf("%d is the magic number", magic);
}
else if(guess > magic)
printf("Wrong, too high");
else printf("Wrong, too low");
return 0;
}
The ? Alternative
You can use the ? operator to replace if-else statements of the general form:
if(condition) expression;
else expression;
However, the target of both if and else must be a single expression—not another
statement.
The ? is called a ternary operator because it requires three operands. It takes the
general form
Exp1 ? Exp2 : Exp3
where Exp1, Exp2, and Exp3 are expressions. Notice the use and placement of the colon.
The value of a ? expression is determined as follows: Exp1 is evaluated. If it is true,
Exp2 is evaluated and becomes the value of the entire ? expression. If Exp1 is false, then
Exp3 is evaluated and its value becomes the value of the expression. For example,
consider x = 10;
y = x>9 ? 100 : 200;
In this example, y is assigned the value 100. If x had been less than 9, y would have
received the value 200. The same code written with the if-else statement would be
x = 10;
if(x>9) y = 100;
else y = 200;
The following program uses the ? operator to square an integer value entered by
the user. However, this program preserves the sign (10 squared is 100 and −10 squared
is −100).
#include <stdio.h>
int main(void)
{
int isqrd, i;
printf("Enter a number: ");
scanf("%d", &i);
isqrd = i>0 ? i*i : -(i*i);
printf("%d squared is %d", i, isqrd);
return 0;
}
The use of the ? operator to replace if-else statements is not restricted to
assignments only. Remember, all functions (except those declared as void) may return
a value. Thus, you can use one or more function calls in a ? expression. When the
function's name is encountered, the function is executed so that its return value may be
determined. Therefore, you can execute one or more function calls using the ? operator
by placing the calls in the expressions that form the ?'s operands. Here is an example.
#include <stdio.h>
int f1(int n);
int f2(void);
int main(void)
{
int t;
printf("Enter a number: ");
scanf("%d", &t);
/* print proper message */
t ? f1(t) + f2() : printf("zero entered.\n");
return 0;
}
int f1(int n)
{
printf("%d ", n);
return 0;
}
int f2(void)
{
printf("entered.\n");
return 0;
}
Entering a 0 in this example calls the printf() function and displays the message zero
entered. If you enter any other number, both f1() and f2() execute. Note that the value
of the ? expression is discarded in this example. You don't need to assign it to anything.
A word of warning: Some C++ compilers rearrange the order of evaluation of an
expression in an attempt to optimize the object code. This could cause functions that
form the operands of the ? operator to execute in an unintended sequence.
Using the ? operator, you can rewrite the magic number program yet again.
/* Magic number program #5. */
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int magic;
int guess;
magic = rand(); /* generate the magic number */
printf("Guess the magic number: ");
scanf("%d", &guess);
if(guess == magic) {
printf("** Right ** ");
printf("%d is the magic number", magic);
}
else
guess > magic ? printf("High") : printf("Low");
return 0;
}
Here, the ? operator displays the proper message based on the outcome of the test
guess > magic.

Spacing and Parentheses

You can add tabs and spaces to expressions to make them easier to read. For example,
the following two expressions are the same:
x=10/y~(127/x);
x = 10 / y ~(127/x);
Redundant or additional parentheses do not cause errors or slow down the execution
of an expression. You should use parentheses to clarify the exact order of evaluation,
both for yourself and for others. For example, which of the following two expressions
is easier to read?
x = y/3-34*temp+127;
x = (y/3) - (34*temp) + 127;

Type Casts in C C++

You can force an expression to be of a specific type by using a cast. The general form of
a cast is
(type) expression
where type is a valid data type. For example, to make sure that the expression x/2
evaluates to type float, write
(float) x/2
Casts are technically operators. As an operator, a cast is unary and has the same
precedence as any other unary operator.
Although casts are not usually used a great deal in programming, they can be very
useful when needed. For example, suppose you wish to use an integer for loop control,
yet to perform computation on it requires a fractional part, as in the following
program:
#include <stdio.h>
int main(void) /* print i and i/2 with fractions */
{
int i;
for(i=1; i<=100; ++i)
printf("%d / 2 is: %f\n", i, (float) i /2);
return 0;
}
Without the cast (float), only an integer division would have been performed. The cast
ensures that the fractional part of the answer is displayed.

Expressions in C C++

Operators, constants, and variables are the constituents of expressions. An expression in
C/C++ is any valid combination of these elements. Because most expressions tend to
follow the general rules of algebra, they are often taken for granted. However, a few
aspects of expressions relate specifically to C and C++.

Order of Evaluation
Neither C nor C++ specifies the order in which the subexpressions of an expression are
evaluated. This leaves the compiler free to rearrange an expression to produce more
optimal code. However, it also means that your code should never rely upon the order
in which subexpressions are evaluated. For example, the expression
x = f1() + f2();
does not ensure that f1() will be called before f2() .

Type Conversion in Expressions
When constants and variables of different types are mixed in an expression, they are
all converted to the same type. The compiler converts all operands up to the type of
the largest operand, which is called type promotion. First, all char and short int values
are automatically elevated to int. (This process is called integral promotion.) Once this
step has been completed, all other conversions are done operation by operation, as
described in the following type conversion algorithm:
IF an operand is a long double
THEN the second is converted to long double
ELSE IF an operand is a double
THEN the second is converted to double
ELSE IF an operand is a float
THEN the second is converted to float
ELSE IF an operand is an unsigned long
THEN the second is converted to unsigned long
ELSE IF an operand is long
THEN the second is converted to long
ELSE IF an operand is unsigned int
THEN the second is converted to unsigned int

Precedence Summary

Table 2-8 lists the precedence of all operators defined by C. Note that all operators,
except the unary operators and ?, associate from left to right. The unary operators
(*, &, −) and ? associate from right to left.

Highest              ( ) [ ] −> .
                      ! ~ ++ – – (type) * & sizeof
                           * / %
                            + −
                          << >>
                           < <= > >=
                          == !=
                              &
                          ^
                            |
                         &&
                          ||
                               ?:
                    = += −=*= /= etc.
Lowest ,

Table 2-8. The Precedence of C Operators


The [ ] and ( ) Operators

Parentheses are operators that increase the precedence of the operations inside them.
Square brackets perform array indexing (arrays are discussed fully in Chapter 4).
Given an array, the expression within square brackets provides an index into that
array. For example,
#include <stdio.h>
char s[80];
int main(void)
{
s[3] = 'X';
printf("%c", s[3]);
return 0;
}
first assigns the value 'X' to the fourth element (remember, all arrays begin at 0) of
array s, and then prints that element.

The Compile-Time Operator sizeof

sizeof is a unary compile-time operator that returns the length, in bytes, of the variable
or parenthesized type-specifier that it precedes. For example, assuming that integers
are 4 bytes and doubles are 8 bytes,
double f;
printf("%d ", sizeof f);
printf("%d", sizeof(int));
will display 8 4.
Remember, to compute the size of a type, you must enclose the type name in
parentheses. This is not necessary for variable names, although there is no harm done
if you do so.

C/C++ defines (using typedef) a special type called size_t, which corresponds
loosely to an unsigned integer. Technically, the value returned by sizeof is of type
size_t. For all practical purposes, however, you can think of it (and use it) as if it were
an unsigned integer value.
sizeof primarily helps to generate portable code that depends upon the size of the
built-in data types. For example, imagine a database program that needs to store six
integer values per record. If you want to port the database program to a variety of
computers, you must not assume the size of an integer, but must determine its actual
length using sizeof. This being the case, you could use the following routine to write a
record to a disk file:
/* Write 6 integers to a disk file. */
void put_rec(int rec[6], FILE *fp)
{
int len;
len = fwrite(rec, sizeof(int)*6, 1, fp);
if(len != 1) printf("Write Error");
}
Coded as shown, put_rec() compiles and runs correctly in any environment, including
those that use 16- and 32-bit integers.
One final point: sizeof is evaluated at compile time, and the value it produces is
treated as a constant within your program.

The & and * Pointer Operators

A pointer is the memory address of some object. A pointer variable is a variable that is
specifically declared to hold a pointer to an object of its specified type. Knowing a
variable's address can be of great help in certain types of routines. However, pointers
have three main functions in C/C++. They can provide a fast means of referencing
array elements. They allow functions to modify their calling parameters. Lastly,
they support linked lists and other dynamic data structures. Chapter 5 is devoted
exclusively to pointers. However, this chapter briefly covers the two operators that
are used to manipulate pointers.
The first pointer operator is &, a unary operator that returns the memory address of
its operand. (Remember, a unary operator only requires one operand.) For example,
m = &count;
places into m the memory address of the variable count. This address is the computer's
internal location of the variable. It has nothing to do with the value of count. You can
think of & as meaning "the address of." Therefore, the preceding assignment statement
means "m receives the address of count."
To better understand this assignment, assume that the variable count is at memory
location 2000. Also assume that count has a value of 100. Then, after the previous
assignment, m will have the value 2000.
The second pointer operator is *, which is the complement of &. The * is a unary
operator that returns the value of the variable located at the address that follows it. For
example, if m contains the memory address of the variable count,
q = *m;
places the value of count into q. Now q has the value 100 because 100 is stored at
location 2000, the memory address that was stored in m. Think of * as meaning
"at address." In this case, you could read the statement as "q receives the value at
address m."
Unfortunately, the multiplication symbol and the "at address" symbol are the
same, and the symbol for the bitwise AND and the "address of" symbol are the same.
These operators have no relationship to each other. Both & and * have a higher
precedence than all other arithmetic operators except the unary minus, with which
they share equal precedence.
Variables that will hold memory addresses (i.e., pointers), must be declared by
putting * in front of the variable name. This indicates to the compiler that it will hold a
pointer. For example, to declare ch as a pointer to a character, write
char *ch;
Here, ch is not a character but a pointer to a character—there is a big difference. The
type of data that a pointer points to, in this case char, is called the base type of the
pointer. However, the pointer variable itself is a variable that holds the address to an
object of the base type. Thus, a character pointer (or any pointer) is of sufficient size
to hold an address as defined by the architecture of the computer that it is running on.
However, remember that a pointer should only point to data that is of that pointer's
base type.
You can mix both pointer and nonpointer variables in the same declaration
statement. For example,
int x, *y, count;
declares x and count as integer types and y as a pointer to an integer type.
The following program uses * and & operators to put the value 10 into a variable
called target. As expected, this program displays the value 10 on the screen.
#include <stdio.h>
int main(void)
{
int target, source;
int *m;
source = 10;
m = &source;
target = *m;
printf("%d", target);
return 0;
}

The ? Operator Conditional or ternary operator

C/C++ contains a very powerful and convenient operator that replaces certain
statements of the if-then-else form. The ternary operator ? takes the general form
Exp1 ? Exp2 : Exp3;
where Exp1, Exp2, and Exp3 are expressions. Notice the use and placement of the colon.
The ? operator works like this: Exp1 is evaluated. If it is true, Exp2 is evaluated
and becomes the value of the expression. If Exp1 is false, Exp3 is evaluated and its
value becomes the value of the expression. For example, in
x = 10;
y = x>9 ? 100 : 200;
y is assigned the value 100. If x had been less than 9, y would have received the value
200. The same code written using the if-else statement is
x = 10;
if(x>9) y = 100;
else y = 200;
The ? operator will be discussed more fully in Chapter 3 in relationship to the other
conditional statements.

Bitwise Operators

Unlike many other languages, C/C++ supports a full complement of bitwise
operators. Since C was designed to take the place of assembly language for most
programming tasks, it needed to be able to support many operations that can be done
in assembler, including operations on bits. Bitwise operation refers to testing, setting, or
shifting the actual bits in a byte or word, which correspond to the char and int data
types and variants. You cannot use bitwise operations on float, double, long double,
void, bool, or other, more complex types. Table 2-6 lists the operators that apply to
bitwise operations. These operations are applied to the individual bits of the
operands.

Operator          Action
&                      AND
|                        OR
^                Exclusive OR (XOR)
~                 One's complement (NOT)
>>                  Shift right
<<                    Shift left

Table 2-6. Bitwise Operators

The bitwise AND, OR, and NOT (one's complement) are governed by the same
truth table as their logical equivalents, except that they work bit by bit. The exclusive
OR has the truth table shown here:
p   q    p^q
0   0     0
1   0     1
1   1     0
0   1     1
As the table indicates, the outcome of an XOR is true only if exactly one of the
operands is true; otherwise, it is false.
Bitwise operations most often find application in device drivers—such as modem
programs, disk file routines, and printer routines —because the bitwise operations
can be used to mask off certain bits, such as parity. (The parity bit confirms that the
rest of the bits in the byte are unchanged. It is usually the high-order bit in each byte.)
Think of the bitwise AND as a way to clear a bit. That is, any bit that is 0 in either
operand causes the corresponding bit in the outcome to be set to 0. For example, the
following function reads a character from the modem port and resets the parity bit to 0:
char get_char_from_modem(void)
{
char ch;
ch = read_modem(); /* get a character from the
modem port */
return(ch & 127);
}
Parity is often indicated by the eighth bit, which is set to 0 by ANDing it with a
byte that has bits 1 through 7 set to 1 and bit 8 set to 0. The expression ch & 127 means
to AND together the bits in ch with the bits that make up the number 127. The net
result is that the eighth bit of ch is set to 0. In the following example, assume that ch
had received the character "A" and had the parity bit set:
The bitwise OR, as the reverse of AND, can be used to set a bit. Any bit that is set
to 1 in either operand causes the corresponding bit in the outcome to be set to 1. For
example, the following is 128 | 3:
Ill 2-2
An exclusive OR, usually abbreviated XOR, will set a bit on if and only if the bits
being compared are different. For example, 127 ^120 is
Remember, relational and logical operators always produce a result that is either
true or false, whereas the similar bitwise operations may produce any arbitrary value
in accordance with the specific operation. In other words, bitwise operations may
produce values other than 0 or 1, while logical operators will always evaluate to 0 or 1.
The bit-shift operators, >> and <<, move all bits in a variable to the right or left as
specified. The general form of the shift-right statement is
 
1 0 0 0 0 0 0 0 128 in binary
0 0 0 0 0 0 1 1 3 in binary
¦___________ bitwise OR
1 0 0 0 0 0 1 1 result
0 1 1 1 1 1 1 1 127 in binary
0 1 1 1 1 0 0 0 120 in binary
^___________ bitwise XOR
0 0 0 0 0 1 1 1 result
Parity bit
1 1 0 0 0 0 0 1 ch containing an "A" with parity set
0 1 1 1 1 1 1 1 127 in binary
&___________ bitwise AND
0 1 0 0 0 0 0 1 "A"without parity
The bitwise OR, as the reverse of AND, can be used to set a bit. Any bit that is set
to 1 in either operand causes the corresponding bit in the outcome to be set to 1. For
example, the following is 128 | 3:
Ill 2-2
An exclusive OR, usually abbreviated XOR, will set a bit on if and only if the bits
being compared are different. For example, 127 ^120 is
Remember, relational and logical operators always produce a result that is either
true or false, whereas the similar bitwise operations may produce any arbitrary value
in accordance with the specific operation. In other words, bitwise operations may
produce values other than 0 or 1, while logical operators will always evaluate to 0 or 1.
The bit-shift operators, >> and <<, move all bits in a variable to the right or left as
specified. The general form of the shift-right statement isvariable >> number of bit positions
The general form of the shift-left statement is
variable << number of bit positions
As bits are shifted off one end, 0's are brought in the other end. (In the case of a
signed, negative integer, a right shift will cause a 1 to be brought in so that the sign bit
is preserved.) Remember, a shift is not a rotate. That is, the bits shifted off one end do
not come back around to the other. The bits shifted off are lost.
Bit-shift operations can be very useful when you are decoding input from an
external device, like a D/A converter, and reading status information. The bitwise shift
operators can also quickly multiply and divide integers. A shift right effectively divides
a number by 2 and a shift left multiplies it by 2, as shown in Table 2-7. The following
program illustrates the shift operators:
/* A bit shift example. */
#include <stdio.h>
int main(void)
{
unsigned int i;
int j;
i = 1;
/* left shifts */
for(j=0; j<4; j++) {
i = i << 1; /* left shift i by 1, which
is same as a multiply by 2 */
printf("Left shift %d: %d\n", j, i);
}
/* right shifts */
for(j=0; j<4; j++) {
i = i >> 1; /* right shift i by 1, which
is same as a division by 2 */
printf("Right shift %d: %d\n", j, i);
}
return 0;
}

unsigned char x;                     x as each statement executes                 value of x
x = 7;                                                   0 0 0 0 0 1 1 1                           7
x = x<<1;                                            0 0 0 0 1 1 1 0                           14
x = x<<3;                                            0 1 1 1 0 0 0 0                          112
x = x<<2;                                           1 1 0 0 0 0 0 0                           192
x = x>>1;                                            0 1 1 0 0 0 0 0                            96
x = x>>2;                                             0 0 0 1 1 0 0 0                           24

*Each left shift multiplies by 2. Notice that information has been lost after x<<2 because
a bit was shifted off the end.
**Each right shift divides by 2. Notice that subsequent divisions do not bring back any
lost bits.
Table 2-7. Multiplication and Division with Shift Operators
The one's complement operator, ~, reverses the state of each bit in its operand. That
is, all 1's are set to 0, and all 0's are set to 1.
The bitwise operators are often used in cipher routines. If you want to make a disk
file appear unreadable, perform some bitwise manipulations on it. One of the simplest
methods is to complement each byte by using the one's complement to reverse each bit
in the byte, as is shown here:
Notice that a sequence of two complements in a row always produces the original
number. Thus, the first complement represents the coded version of that byte. The
second complement decodes the byte to its original value.
You could use the encode() function shown here to encode a character.
/* A simple cipher function. */
char encode(char ch)
{return(~ch); /* complement it */
}
Of course, a file encoded using encode() would be very easy to crack!