Showing posts with label Conditional. Show all posts
Showing posts with label Conditional. Show all posts

Tuesday, September 21, 2010

The Conditional Expression

Sometimes newcomers to C/C++ are confused by the fact that you can use any valid
expression to control the if or the ? operator. That is, you are not restricted to
expressions involving the relational and logical operators (as is the case in languages
like BASIC or Pascal). The expression must simply evaluate to either a true or false
(zero or nonzero) value. For example, the following program reads two integers from
the keyboard and displays the quotient. It uses an if statement, controlled by the
second number, to avoid a divide-by-zero error.
/* Divide the first number by the second. */
#include <stdio.h>
int main(void)
{
int a, b;
printf("Enter two numbers: ");
scanf("%d%d", &a, &b);
if(b) printf("%d\n", a/b);
else printf("Cannot divide by zero.\n");
return 0;
}
This approach works because if b is 0, the condition controlling the if is false and the
else executes. Otherwise, the condition is true (nonzero) and the division takes place.
One other point: Writing the if statement as shown here
if(b != 0) printf("%d\n", a/b);
is redundant, potentially inefficient, and is considered bad style. Since the value of b
alone is sufficient to control the if, there is no need to test it against 0.

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 Dot (.) and Arrow (−>) Operators

In C, the . (dot) and the −>(arrow) operators access individual elements of structures
and unions. Structures and unions are compound (also called aggregate) data types that
may be referenced under a single name (see Chapter 7). In C++, the dot and arrow
operators are also used to access the members of a class.
The dot operator is used when working with a structure or union directly. The
arrow operator is used when a pointer to a structure or union is used. For example,
given the fragment
struct employee
{
char name[80];
int age;
float wage;
} emp;
struct employee *p = &emp; /* address of emp into p */
you would write the following code to assign the value 123.23 to the wage member of
structure variable emp:
emp.wage = 123.23;
However, the same assignment using a pointer to emp would be
p->wage = 123.23;

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.