Showing posts with label Octal. Show all posts
Showing posts with label Octal. Show all posts

Tuesday, September 21, 2010

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 ? 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!

Arithmetic Operators

Table 2-4 lists C/C++'s arithmetic operators. The operators +, −, *, and / work as they
do in most other computer languages. You can apply them to almost any built-in data
type. When you apply / to an integer or character, any remainder will be truncated.
For example, 5/2 will equal 2 in integer division.
The modulus operator % also works in C/C++ as it does in other languages,
yielding the remainder of an integer division. However, you cannot use it on
floating-point types. The following code fragment illustrates %:
int x, y;
x = 5;
y = 2;
printf("%d ", x/y); /* will display 2 */
printf("%d ", x%y); /* will display 1, the remainder of
the integer division */
x = 1;
y = 2;
printf("%d %d", x/y, x%y); /* will display 0 1 */
The last line prints a 0 and a 1 because 1/2 in integer division is 0 with a remainder of 1.
The unary minus multiplies its operand by –1. That is, any number preceded by a
minus sign switches its sign.

Increment and Decrement
C/C++ includes two useful operators not generally found in other computer
languages. These are the increment and decrement operators, ++ and −−. The operator
++ adds 1 to its operand, and −− subtracts one. In other words:
x = x+1;
is the same as
++x;
and
x = x-1;
is the same as
x--;
Both the increment and decrement operators may either precede (prefix) or follow
(postfix) the operand. For example,
x = x+1;
can be written
++x;
or
x++;
There is, however, a difference between the prefix and postfix forms when you use
these operators in an expression. When an increment or decrement operator precedes
its operand, the increment or decrement operation is performed before obtaining the
value of the operand for use in the expression. If the operator follows its operand,
 
Operator                             Action
−                             Subtraction, also unary minus
+                                          Addition
*                                       Multiplication
/                                           Division
%                                        Modulus
– –                                     Decrement
++                                      Increment

Table 2-4. Arithmetic Operators

the value of the operand is obtained before incrementing or decrementing it. For
instance,
x = 10;
y = ++x;
sets y to 11. However, if you write the code as
x = 10;
y = x++;
y is set to 10. Either way, x is set to 11; the difference is in when it happens.
Most C/C++ compilers produce very fast, efficient object code for increment and
decrement operations—code that is better than that generated by using the equivalent
assignment statement. For this reason, you should use the increment and decrement
operators when you can.
Here is the precedence of the arithmetic operators:
highest ++ – –
– (unary minus)
* / %
lowest + –
Operators on the same level of precedence are evaluated by the compiler from left to
right. Of course, you can use parentheses to alter the order of evaluation. C/C++ treats
parentheses in the same way as virtually all other computer languages. Parentheses
force an operation, or set of operations, to have a higher level of precedence.

Relational and Logical Operators
In the term relational operator, relational refers to the relationships that values can
have with one another. In the term logical operator, logical refers to the ways these
relationships can be connected. Because the relational and logical operators often
work together, they are discussed together here.
The idea of true and false underlies the concepts of relational and logical operators.
In C, true is any value other than zero. False is zero. Expressions that use relational or
logical operators return 0 for false and 1 for true.
C++ fully supports the zero/non-zero concept of true and false. However, it also
defines the bool data type and the Boolean constants true and false. In C++, a 0 value
is automatically converted into false, and a non-zero value is automatically converted
into true. The reverse also applies: true converts to 1 and false converts to 0. In C++,

the outcome of a relational or logical operation is true or false. But since this
automatically converts into 1 or 0, the distinction between C and C++ on this issue is
mostly academic.
Table 2-5 shows the relational and logical operators. The truth table for the logical
operators is shown here using 1's and 0's.
p             q               p && q                 p || q               !p
0             0                    0                          0                 1
0             1                    0                          1                1
1             1                    1                          1                0
1             0                    0                          1                0
Both the relational and logical operators are lower in precedence than the
arithmetic operators. That is, an expression like 10 > 1+12 is evaluated as if it were
written 10 > (1+12). Of course, the result is false.
You can combine several operations together into one expression, as shown here:
10>5 && !(10<9) || 3<=4

Relational Operators
Operator                     Action
>                               Greater than
>=                        Greater than or equal
<                               Less than
<=                       Less than or equal
= =                               Equal
!=                                Not equal

Logical Operators
Operator                       Action
&&                                AND
||                                      OR
!                                     NOT

Table 2-5. Relational and Logical Operators

In this case, the result is true.
Although neither C nor C++ contain an exclusive OR (XOR) logical operator, you
can easily create a function that performs this task using the other logical operators.
The outcome of an XOR operation is true if and only if one operand (but not both) is
true. The following program contains the function xor() , which returns the outcome of
an exclusive OR operation performed on its two arguments:
#include <stdio.h>
int xor(int a, int b);
int main(void)
{
printf("%d", xor(1, 0));
printf("%d", xor(1, 1));
printf("%d", xor(0, 1));
printf("%d", xor(0, 0));
return 0;
}
/* Perform a logical XOR operation using the
two arguments. */
int xor(int a, int b)
{
return (a || b) && !(a && b);
}
The following table shows the relative precedence of the relational and logical
operators:
Highest
!
> >= < <=
== !=
&&
Lowest
  ||

As with arithmetic expressions, you can use parentheses to alter the natural order of
evaluation in a relational and/or logical expression. For example, 

!0&& 0 || 0
is false. However, when you add parentheses to the same expression, as shown here,
the result is true:
!(0 && 0) || 0
Remember, all relational and logical expressions produce either a true or false
result. Therefore, the following program fragment is not only correct, but will print
the number 1.
int x;
x = 100;
printf("%d", x>10);

The Assignment Operator

You can use the assignment operator within any valid expression. This is not the
case with most computer languages (including Pascal, BASIC, and FORTRAN), which
treat the assignment operator as a special case statement. The general form of the
assignment operator is
variable_name = expression;
where an expression may be as simple as a single constant or as complex as you
require. C/C++ uses a single equal sign to indicate assignment (unlike Pascal or
Modula-2, which use the := construct). The target, or left part, of the assignment
must be a variable or a pointer, not a function or a constant.
Frequently in literature on C/C++ and in compiler error messages you will see
these two terms: lvalue and rvalue. Simply put, an lvalue is any object that can occur
on the left side of an assignment statement. For all practical purposes, "lvalue" means
"variable." The term rvalue refers to expressions on the right side of an assignment and
simply means the value of an expression.

Type Conversion in Assignments

When variables of one type are mixed with variables of another type, a type conversion
will occur. In an assignment statement, the type conversion rule is easy: The value of
the right side (expression side) of the assignment is converted to the type of the left
side (target variable), as illustrated here:
int x;
char ch;
float f;
void func(void)
{
ch = x; /* line 1 */
x = f; /* line 2 */
f = ch; /* line 3 */
f = x; /* line 4 */
}
In line 1, the left high-order bits of the integer variable x are lopped off, leaving ch with
the lower 8 bits. If x were between 255 and 0, ch and x would have identical values.
Otherwise, the value of ch would reflect only the lower-order bits of x. In line 2, x will
receive the nonfractional part of f. In line 3, f will convert the 8-bit integer value stored
in ch to the same value in the floating-point format. This also happens in line 4, except
that f will convert an integer value into floating-point format.
When converting from integers to characters and long integers to integers, the
appropriate amount of high-order bits will be removed. In many 16-bit environments,
this means that 8 bits will be lost when going from an integer to a character and 16 bits
will be lost when going from a long integer to an integer. For 32-bit environments, 24
bits will be lost when converting from an integer to a character and 16 bits will be lost
when converting from an integer to a short integer.
Table 2-3 summarizes the assignment type conversions. Remember that the
conversion of an int to a float, or a float to a double, and so on, does not add any precision or accuracy. These kinds of conversions only change the form in which the
value is represented. In addition, some compilers always treat a char variable as
positive, no matter what value it has, when converting it to an int or float. Other
compilers treat char variable values greater than 127 as negative numbers when
converting. Generally speaking, you should use char variables for characters, and use
ints, short ints, or signed chars when needed to avoid possible portability problems.
To use Table 2-3 to make a conversion not shown, simply convert one type at a time
until you finish. For example, to convert from double to int, first convert from double
to float and then from float to int.

Multiple Assignments
C/C++ allows you to assign many variables the same value by using multiple
assignments in a single statement. For example, this program fragment assigns x, y,
and z the value 0:
x = y = z = 0;

Target                             Type Expression                          Type Possible Info Loss
signed char                        char                                   If value > 127, target is negative
char                                 short int                                              High-order 8 bits
char                                  int (16 bits)                                      High-order 8 bits
char                                  int (32 bits)                                        High-order 24 bits
char                                long int                                            High-order 24 bits
short int                             int (16 bits)                                                  None
short int                          int (32 bits)                                      High-order 16 bits
int (16 bits)                     long int                                            High-order 16 bits
int (32 bits)                     long int                                                    None
int                                     float                                     Fractional part and possibly more
float                                double                             Precision, result rounded
double                            long                                  double Precision, result rounded

Table 2-3. The Outcome of Common Type Conversions

String and Backslash Character Constants

String Constants

C/C++ supports one other type of constant: the string. A string is a set of characters
enclosed in double quotes. For example, "this is a test" is a string. You have seen
examples of strings in some of the printf() statements in the sample programs.
Although C allows you to define string constants, it does not formally have a string
data type. (C++ does define a string class, however.)
You must not confuse strings with characters. A single character constant is
enclosed in single quotes, as in 'a'. However, "a" is a string containing only one letter.

Backslash Character Constants

Enclosing character constants in single quotes works for most printing characters. A
few, however, such as the carriage return, are impossible to enter into a string from the
keyboard. For this reason, C/C++ include the special backslash character constants
shown in Table 2-2 so that you may easily enter these special characters as constants.
These are also referred to as escape sequences. You should use the backslash codes
instead of their ASCII equivalents to help ensure portability.
For example, the following program outputs a new line and a tab and then prints
the string This is a test.
#include <stdio.h>
int main(void)
{
printf("\n\tThis is a test.");
return 0;
}

Code               Meaning
\b                    Backspace
\f                     Form feed
\n                     New line
\r                  Carriage return
\t                  Horizontal tab
\"                  Double quote
\'                  Single quote
\0                      Null
\\                   Backslash
\v                  Vertical tab
\a                      Alert
\?                Question mark
\N             Octal constant (where N is an octal constant)
\xN           Hexadecimal constant (where N is a hexadecimalconstant)

Table 2-2. Backslash Codes

Hexadecimal and Octal Constants

It is sometimes easier to use a number system based on 8 or 16 rather than 10 (our
standard decimal system). The number system based on 8 is called octal and uses the digits 0 through 7. In octal, the number 10 is the same as 8 in decimal. The base 16
number system is called hexadecimal and uses the digits 0 through 9 plus the letters
A through F, which stand for 10, 11, 12, 13, 14, and 15, respectively. For example, the
hexadecimal number 10 is 16 in decimal. Because these two number systems are
used frequently, C/C++ allows you to specify integer constants in hexadecimal or octal
instead of decimal. A hexadecimal constant must consist of a 0x followed by the
constant in hexadecimal form. An octal constant begins with a 0. Here are some
examples:
int hex = 0x80; /* 128 in decimal */
int oct = 012; /* 10 in decimal */