OPERATORS AND EXPRESSIONS


EXPRESSIONS AND OPERATORS


One reason for the power of C is its wide range of useful operators.

An operator is a function which is applied to values to give a result.

You should be familiar with operators such as +, -, /.

Arithmetic operators are the most common.

Other operators are used for comparison of values, combination of logical states, and manipulation of individual binary digits.
The binary operators are rather low level for so are not covered here.

Operators and values are combined to form expressions.

The values produced by these expressions can be stored in variables, or used as a part of even larger expressions.


OPERATORS

ARITHMETIC OPERATORS


Here are the most common arithmetic operators

?

*, / and % will be performed before + or - in any expression.

Brackets can be used to force a different order of evaluation to this.

Where division is performed between two integers, the result will be an integer, with remainder discarded.
Modulo reduction is only meaningful between integers.

If a program is ever required to divide a number by zero, this will cause an error, usually causing the program to crash.
Here are some arithmetic expressions used within assignment statements.


VELOCITY = DISTANCE / TIME;

FORCE = MASS * ACCELERATION;

COUNT = COUNT + 1;



C has some operators which allow abbreviation of certain types of arithmetic assignment statements.

?


These operations are usually very efficient. They can be combined with another expression.


?



Versions where the operator occurs before the variable name change the value of the variable before evaluating the expression, so


?



These can cause confusion if you try to do too many things on one command line.
You are recommended to restrict your use of ++ and -- to ensure that your programs stay readable.


Another shorthand notation is listed below


?



These are simple to read and use.

ORDER OF PRECEDENCE



?



It is necessary to be careful of the meaning of such expressions as  a + b * c
We may want the effect as either


   (A + B) * C

OR

   A + (B * C)


All operators have a priority, and high priority operators are evaluated before lower priority ones.
Operators of the same priority are evaluated from left to right, so that


   A - B - C

is evaluated as

   ( A - B ) - C


as you would expect.


From high priority to low priority the order for all C operators.

CONDITIONALS



This Chapter deals with the various methods that C can control the flow of logic in a program.
Apart from slight syntactic variation they are similar to other languages.


The ? Operator

The ? (ternary condition) operator is a more efficient form for expressing simple if statements. It has the following form:

EXPRESSION1 ? EXPRESSION2: EXPRESSION3


It simply states:

IF EXPRESSION1 THEN EXPRESSION2 ELSE EXPRESSION3

For example to assign the maximum of a and b to z:


Z = (A>B) ? A : B;


which is the same as:


IF (A>B)
Z = A;
ELSE
Z=B;


LOGICAL CONNECTORS


These are the usual And, Or and Not operators.


? .


They are frequently used to combine relational operators, for example


X < 20 && X >= 10


In C these logical connectives employ a technique known as lazy evaluation. They evaluate their left hand operand, and then only evaluate the right hand one if this is required.


Clearly false && anything is always false, true || anything is always true. In such cases the second test is not evaluated.


Not operates on a single logical value, its effect is to reverse its state. Here is an example of its use.


IF ( ! ACCEPTABLE )
PRINTF("NOT ACCEPTABLE !!\N");



RELATIONAL OPERATORS


C has no special type to represent logical or boolean values.
It improvises by using any of the integral types char, int, short, long, unsigned, with a value of 0 representing false and any other value representing true.


It is rare for logical values to be stored in variables.


They are usually generated as required by comparing two numeric values. This is where the comparison operators are used, they compare two numeric values and produce a logical result.


?



Note


That == is used in comparisons and = is used in assignments. Comparison operators are used in expressions like the ones below.


X == Y
I > 10
A + B != C


In the last example, all arithmetic is done before any comparison is made.
These comparisons are most frequently used to control an if statement or a for or a while loop. These will be introduced in a later chapter.


INCREMENT AND DECREMENT OPERATORS


The increment (++) and decrement (--) operators simply add 1 or subtract 1 from a variable and return the value of that variable.
These are unary operators meaning they work on only one operand, which is the variable to modify.
There are two ways to use these operators – prefix or postfix.

That means you can put the operator in front of the variable like ++a or behind the variable like b--. However, they have slightly different behaviour.
In prefix notation the variable is incremented or decremented first, then the new value is returned. In postfix notation the variable's original value is returned, and the addition or subtraction happens later.

LET US SEE SOME EXAMPLES:

Sample Code

BORDER=

#INCLUDE

VOID MAIN()
{
INT A = 7;
INT B;

++A; /* A IS NOW 8 */
A++; /* A IS NOW 9 */
B = A++;
PRINTF( "A = %D, B = %DN", A, B );

B = ++A;
PRINTF( "A = %D, B = %DN", A, B );

FLOAT F = 4.05;
FLOAT PI = --F + 0.09159;
PRINTF( "F = %F, PI = %.5FN", F, PI );
}


Here is the output:

A = 10, B = 9
A = 11, B = 11
F = 3.050000, PI = 3.14159



Lines 8 and 9 show using prefix and postfix notation, and since these lines consist only of the increment operations, there is no difference between them.
On line 10, using the postfix increment operator, b is assigned the value of a before a is incremented.

The increment happens at the end of the statement – you can think of it as happening at the ';' mark.

More precisely, C has places called “sequence points” at which side effects of operations like the postfix ++ and – are processed, and the end of a statement is one of those places.

In the first printed line you can see that b has the value of a before it was incremented.

On line 13 the prefix increment operator causes a to be incremented first, then that value assigned to b. The second printed line shows that both a and b have the same value.

Lines 16-17 show that you can use these operators with real number variables as well, and you can combine these operators with others.

Be careful writing complicated statements with postfix operators, they might not behave as you expect.

block after the while loop to be able to see the last node's information printed out. C Increment and Decrement Operators

The increment (++) and decrement (--) operators simply add 1 or subtract 1 from a variable and return the value of that variable. These are unary operators meaning they work on only one operand, which is the variable to modify.

There are two ways to use these operators – prefix or postfix. That means you can put the operator in front of the variable like ++a or behind the variable like b--. However, they have slightly different behaviour.

In prefix notation the variable is incremented or decremented first, then the new value is returned. In postfix notation the variable's original value is returned, and the addition or subtraction happens later.



BITWISE OPERATORS


The bitwise operators of C a summarised in the following table:


?


NOTE:


DO NOT confuse & with &&: & is bitwise AND, && logical AND.

?is a unary operator -- it only operates on one argument to right of the operator.

The shift operators perform appropriate shift by operator on the right to the operator on the left. The right operator must be positive. The vacated bits are filled with zero (i.e. There is NO wrap around).



For example:

x << 2 shifts the bits in x by 2 places to the left. So: if x = 00000010 (binary) or 2 (decimal) then: ?or 0 (decimal) Also: if x = 00000010 (binary) or 2 (decimal) ?or 8 (decimal)


Therefore a shift left is equivalent to a multiplication by 2.


Similarly a shift right is equal to division by 2


NOTE:


Shifting is much faster than actual multiplication (*) or division (/) by 2. So if you want fast multiplications or division by 2 use shifts.

To illustrate many points of bitwise operators let us write a function, Bitcount, that counts bits set to 1 in an 8 bit number (unsigned char) passed as an argument to the function.


INT BITCOUNT(UNSIGNED CHAR X)

{ INT COUNT;

FOR (COUNT=0; X != 0; X>>=1)

IF ( X & 01)

COUNT++;

RETURN COUNT;

}


This function illustrates many C program points:

for loop not used for simple counting operation

x? x = x >> 1

for loop will repeatedly shift right x until x becomes 0

use expression evaluation of x & 01 to control if

x & 01 masks of 1st bit of x if this is 1 then count++

BIT FIELDS


Bit Fields allow the packing of data in a structure. This is especially useful when memory or data storage is at a premium. Typical examples:


Packing several objects into a machine word. e.g. 1 bit flags can be compacted -- Symbol tables in compilers.


Reading external file formats -- non-standard file formats could be read in. E.g. 9 bit integers.
C lets us do this in a structure definition by putting :bit length after the variable. i.e.


STRUCT PACKED_STRUCT {
UNSIGNED INT F1:1;
UNSIGNED INT F2:1;
UNSIGNED INT F3:1;
UNSIGNED INT F4:1;
UNSIGNED INT TYPE:4;
UNSIGNED INT FUNNY_INT:9;
} PACK;


Here the packed_struct contains 6 members: Four 1 bit flags f1..f3, a 4 bit type and a 9 bit funny_int.
C automatically packs the above bit fields as compactly as possible, provided that the maximum length of the field is less than or equal to the integer word length of the computer.

If this is not the case then some compilers may allow memory overlap for the fields whilst other would store the next field in the next word (see comments on bit fiels portability below).

Access members as usual via:

   PACK.TYPE = 7;



NOTE:


ONLY N LOWER BITS WILL BE ASSIGNED TO AN N BIT NUMBER. SO TYPE CANNOT TAKE VALUES LARGER THAN 15 (4 BITS LONG).

BIT FIELDS ARE ALWAYS CONVERTED TO INTEGER TYPE FOR COMPUTATION.


YOU ARE ALLOWED TO MIX ``NORMAL'' TYPES WITH BIT FIELDS.

THE UNSIGNED DEFINITION IS IMPORTANT - ENSURES THAT NO BITS ARE USED AS A ?FLAG.

BIT FIELDS: PRACTICAL EXAMPLE


Frequently device controllers (e.g. disk drives) and the operating system need to communicate at a low level.
Device controllers contain several registers which may be packed together in one integer

?

Disk Controller Register We could define this register easily with bit fields:

STRUCT DISK_REGISTER {
UNSIGNED READY:1;
UNSIGNED ERROR_OCCURED:1;
UNSIGNED DISK_SPINNING:1;
UNSIGNED WRITE_PROTECT:1;
UNSIGNED HEAD_LOADED:1;
UNSIGNED ERROR_CODE:8;
UNSIGNED TRACK:9;
UNSIGNED SECTOR:5;
UNSIGNED COMMAND:5;
};



To access values stored at a particular memory address, DISK_REGISTER_MEMORY we can assign a pointer of the above structure to access the memory via:

STRUCT DISK_REGISTER *DISK_REG = (STRUCT DISK_REGISTER *) DISK_REGISTER_MEMORY;


The disk driver code to access this is now relatively straightforward:


/* DEFINE SECTOR AND TRACK TO START READ */
DISK_REG->SECTOR = NEW_SECTOR;
DISK_REG->TRACK = NEW_TRACK;
DISK_REG->COMMAND = READ;
/* WAIT UNTIL OPERATION DONE, READY WILL BE TRUE */
WHILE ( ! DISK_REG->READY ) ;
/* CHECK FOR ERRORS */
IF (DISK_REG->ERROR_OCCURED)
{ /* INTERROGATE DISK_REG->ERROR_CODE FOR ERROR TYPE */
SWITCH (DISK_REG->ERROR_CODE)
......
}




No comments:

Post a Comment