Showing posts with label Bitwise. Show all posts
Showing posts with label Bitwise. Show all posts

Tuesday, September 21, 2010

Shorthand Assignments

There is a variation on the assignment statement, sometimes referred to as a shorthand
assignment, that simplifies the coding of a certain type of assignment operation. For
example,
x = x+10;
can be written as
x += 10;
The operator += tells the compiler to assign to x the value of x plus 10.
This shorthand works for all the binary operators (those that require two
operands). In general, statements like:
var = var operator expression
can be rewritten as
var operator = expression
For another example,
x = x-100;
is the same as
x -= 100;
Shorthand notation is widely used in professionally written C/C++ programs; you
should become familiar with it.

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!