Interdata_v6/usr/doc/ctut/ct8
.NH
Bit Operators
.PP
C has several operators for logical bit-operations.
For example,
.E1
x = x & 0177;
.E2
forms the bit-wise
.UC AND
of
.UL x
and 0177,
effectively retaining only the last seven bits of
.UL x\*.
Other operators are
.E1
.ft R
\(or inclusive OR
^ (circumflex) exclusive OR
.tr+~
+ (tilde) 1's complement
.tr++
! logical NOT
<< left shift (as in x<<2)
>> right shift (arithmetic on PDP\(hy11; logical on H6070, IBM360)
.E2
.NH
Assignment Operators
.PP
An unusual feature of C
is that the normal binary operators like
`+', `\(mi', etc.
can be combined with the assignment operator `='
to form new assignment operators.
For example,
.E1
.MC
x -= 10;
.E2
uses the assignment operator `\(mi=' to decrement
.UL x
by 10,
and
.E1
x &= 0177
.E2
.mc
forms the
.UC AND
of
.UL x
and 0177.
This convention is a useful notational shortcut,
particularly if
.UL x
is a complicated expression.
The classic example is summing an array:
.E1
for( sum=i=0; i<n; i\*+ )
.MC
sum += array[i];
.mc
.E2
.PP
Because all other operators in an expression are evaluated
before the assignment operator,
the order of evaluation should be watched carefully:
.E1
x = x<<y | z;
.E2
means
``shift
.UL x
left
.UL y
places,
then
.UC OR
with
.UL z,
and store in
.UL x\*.''
But
.E1
.MC
x <<= y | z;
.mc
.E2
means
``shift
.UL x
left by
.UL y|z
places'',
which is rather different.
.NH
Floating Point
.PP
We've skipped over floating point so far,
and the treatment here will be hasty.
C has single and double precision numbers
(where the precision depends on the machine at hand).
For example,
.E1
double sum;
float avg, y[10];
sum = 0\*.0;
for( i=0; i<n; i\*+ )
sum =+ y[i];
avg = sum/n;
.E2
forms the sum and average of the array
.UL y\*.
.PP
All floating arithmetic is done in double precision.
Mixed mode arithmetic is legal;
if an arithmetic operator in an expression
has both operands
.UL int
or
.UL char,
the arithmetic done is integer, but
if one operand is
.UL int
or
.UL char
and the other is
.UL float
or
.UL double,
both operands are converted to
.UL double\*.
Thus if
.UL i
and
.UL j
are
.UL int
and
.UL x
is
.UL float,
.E1
(x+i)/j converts i and j to float
x + i/j does i/j integer, then converts
.E2
Type conversion
may be made by assignment;
for instance,
.E1
int m, n;
float x, y;
m = x;
y = n;
.E2
converts
.UL x
to integer
(truncating toward zero),
and
.UL n
to floating point.
.PP
Floating constants are just like those in Fortran or PL/I,
except that the exponent letter is `e' instead of `E'.
Thus:
.E1
pi = 3\*.14159;
large = 1\*.23456789e10;
.E2
.PP
.UL printf
will format floating point numbers:
.UL ``%w\*.df''
in the format string will print the corresponding variable
in a field
.UL w
digits wide, with
.UL d
decimal places.
An
.UL e
instead of an
.UL f
will produce exponential notation.
.NH
Horrors! goto's and labels
.PP
C has
a
.UL goto
statement and labels, so you can branch about
the way you used to.
But most of the time
.UL goto's
aren't needed.
(How many have we used up to this point?)
The code can almost always be more clearly expressed by
.UL for/while,
.UL if/else,
and compound statements.
.PP
One use of
.UL goto's
with some legitimacy is in a program
which
contains a long loop,
where a
.UL while(1)
would be too extended.
Then you might write
.E1
mainloop:
\*.\*.\*.
goto mainloop;
.E2
Another use is to implement a
.UL break
out of more than one level of
.UL for
or
.UL while\*.
.UL goto's
can only branch to labels within the same function.
.NH
Acknowledgements
.PP
I am indebted to a veritable host of readers who made
valuable criticisms on several drafts of this tutorial.
They ranged in experience from complete beginners
through several implementors of C compilers
to the C language designer himself.
Needless to say, this is a wide enough spectrum of opinion
that no one is satisfied (including me);
comments and suggestions are still welcome,
so that some future version might be improved.