0% found this document useful (0 votes)
26 views

Preprocessor & Bitwise Opr

The document discusses the C preprocessor and bitwise operators. It describes three main uses of the preprocessor - directives, constants, and macros. Directives include #include, #define, and conditional compilation directives like #if. Constants can be defined using #define and macros allow code to be expanded at compile time. The document provides examples of defining constants and macros and discusses best practices to avoid unintended behavior. It also covers bitwise operators like left and right shift and explains their effects at the bit level.

Uploaded by

sarthakmalik5098
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
26 views

Preprocessor & Bitwise Opr

The document discusses the C preprocessor and bitwise operators. It describes three main uses of the preprocessor - directives, constants, and macros. Directives include #include, #define, and conditional compilation directives like #if. Constants can be defined using #define and macros allow code to be expanded at compile time. The document provides examples of defining constants and macros and discusses best practices to avoid unintended behavior. It also covers bitwise operators like left and right shift and explains their effects at the bit level.

Uploaded by

sarthakmalik5098
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 8

Ankur Rastogi Unit-V C Pre Processor & Bitwise Operator

The C Preprocessor
The C preprocessor modifies a source code file before handing it over to the compiler. You're
most likely used to using the preprocessor to include files directly into other files, or #define
constants, but the preprocessor can also be used to create "inlined" code using macros
expanded at compile time and to prevent code from being compiled twice.

There are essentially three uses of the preprocessor--directives, constants, and macros.
Directives are commands that tell the preprocessor to skip part of a file, include another file,
or define a constant or macro. Directives always begin with a sharp sign (#) and for
readability should be placed flush to the left of the page. All other uses of the preprocessor
involve processing #define'd constants or macros. Typically, constants and macros are written
in ALL CAPS to indicate they are special.

Header Files
The #include directive tells the preprocessor to grab the text of a file and place it directly into
the current file. Typically, such statements are placed at the top of a program--hence the
name "header file" for files thus included.

Constants
If we write
#define [identifier name] [value]
whenever [identifier name] shows up in the file, it will be replaced by [value].

If you are defining a constant in terms of a mathematical expression, it is wise to surround the
entire value in parentheses:
#define PI_PLUS_ONE (3.14 + 1)

By doing so, you avoid the possibility that an order of operations issue will destroy the
meaning of your constant:
x = PI_PLUS_ONE * 5;
Without parentheses, the above would be converted to
x = 3.14 + 1 * 5;
which would result in 1 * 5 being evaluated before the addition, not after. Oops!

It is also possible to write simply


#define [identifier name]
which defines [identifier name] without giving it a value. This can be useful in conjunction
with another set of directives that allow conditional compilation.

Conditional Compilation

2013 Page 1
Ankur Rastogi Unit-V C Pre Processor & Bitwise Operator

There are a whole set of options that can be used to determine whether the preprocessor will
remove lines of code before handing the file to the compiler. They include #if, #elif, #else,
#ifdef, and #ifndef. An #if or #if/#elif/#else block or a #ifdef or #ifndef block must be
terminated with a closing #endif.

The #if directive takes a numerical argument that evaluates to true if it's non-zero. If its
argument is false, then code until the closing #else, #elif, of #endif will be excluded.

Commenting out Code

Conditional compilation is a particularly useful way to comment out a block of code that
contains multi-line comments (which cannot be nested).
#if 0
/* comment ...
*/

// code

/* comment */
#endif

Example:
#define X 0
void main()
{
#if(X>0)
printf("positive");
#else
printf("negative");
#endif
getch();
}

Macros
The other major use of the preprocessor is to define macros. The advantage of a
macro is that it can be type-neutral (this can also be a disadvantage, of course), and
it's inlined directly into the code, so there isn't any function call overhead.

A macro definition is usually of the following form:


#define MACRO_NAME(arg1, arg2, ...) [code to expand to]

For instance, a simple increment macro might look like this:


#define INCREMENT(x) x++

They look a lot like function calls, but they're not so simple. There are actually a couple of
tricky points when it comes to working with macros. First, remember that the exact text of the
macro argument is "pasted in" to the macro. For instance, if you wrote something like this:
#define MULT(x, y) x * y

2013 Page 2
Ankur Rastogi Unit-V C Pre Processor & Bitwise Operator

and then wrote


int z = MULT(3 + 2, 4 + 2);

what value do you expect z to end up with? The obvious answer, 30, is wrong! That's because
what happens when the macro MULT expands is that it looks like this:
int z = 3 + 2 * 4 + 2; // 2 * 4 will be evaluated first!

So z would end up with the value 13! This is almost certainly not what you want to happen.
The way to avoid it is to force the arguments themselves to be evaluated before the rest of the
macro body. You can do this by surrounding them by parentheses in the macro definition:
#define MULT(x, y) (x) * (y)
// now MULT(3 + 2, 4 + 2) will expand to (3 + 2) * (4 + 2)

But this isn't the only gotcha! It is also generally a good idea to surround the macro's code in
parentheses if you expect it to return a value. Otherwise, you can get similar problems as
when you define a constant. For instance, the following macro, which adds 5 to a given
argument, has problems when embedded within a larger statement:
#define ADD_FIVE(a) (a) + 5

int x = ADD_FIVE(3) * 3;
// this expands to (3) + 5 * 3, so 5 * 3 is evaluated first
// Now x is 18, not 24!
To fix this, you generally want to surround the whole macro body with parentheses to prevent
the surrounding context from affecting the macro body.
#define ADD_FIVE(a) ((a) + 5)

int x = ADD_FIVE(3) * 3;
On the other hand, if you have a multiline macro that you are using for its side effects, rather
than to compute a value, you probably want to wrap it within curly braces so you don't have
problems when using it following an if statement.
// We use a trick involving exclusive-or to swap two variables
#define SWAP(a, b) a ^= b; b ^= a; a ^= b;

int x = 10;
int y = 5;

// works OK
SWAP(x, y);

// What happens now?


if(x < 0)
SWAP(x, y);
When SWAP is expanded in the second example, only the first statement, a ^= b, is governed
by the conditional; the other two statements will always execute. What we really meant was
that all of the statements should be grouped together, which we can enforce using curly
braces:
#define SWAP(a, b) {a ^= b; b ^= a; a ^= b;}
Now, there is still a bit more to our story! What if you write code like so:
#define SWAP(a, b) { a ^= b; b ^= a; a ^= b; }

2013 Page 3
Ankur Rastogi Unit-V C Pre Processor & Bitwise Operator

int x = 10;
int y = 5;
int z = 4;

// What happens now?


if(x < 0)
SWAP(x, y);
else
SWAP(x, z);
Then it will not compile because semicolon after the closing curly brace will break the flow
between if and else. The solution? Use a do-while loop:
#define SWAP(a, b) do { a ^= b; b ^= a; a ^= b; } while ( 0 )

int x = 10;
int y = 5;
int z = 4;

// What happens now?


if(x < 0)
SWAP(x, y);
else
SWAP(x, z);
Now the semi-colon doesn't break anything because it is part of the expression. (By the way,
note that we didn't surround the arguments in parentheses because we don't expect anyone to
pass an expression into swap!)

Multiline macros
Until now, we've seen only short, one line macros (possibly taking advantage of the
semicolon to put multiple statements on one line.) It turns out that by using a the "\" to
indicate a line continuation, we can write our macros across multiple lines to make them a bit
more readable.

For instance, we could rewrite swap as


#define SWAP(a, b) { \
a ^= b; \
b ^= a; \
a ^= b; \
}
Notice that you do not need a slash at the end of the last line! The slash tells the preprocessor
that the macro continues to the next line, not that the line is a continuation from a previous
line.

Aside from readability, writing multi-line macros may make it more obvious that you need to
use curly braces to surround the body because it's more clear that multiple effects are
happening at once.

2013 Page 4
Ankur Rastogi Unit-V C Pre Processor & Bitwise Operator

Bitwise Operators in C
Generally, as a programmer you don't need to concern yourself about operations at the bit level.
You're free to think in bytes, or int’s and double’s, or even higher level data types composed of a
combination of these. But there are times when you'd like to be able to go to the level of an
individual bit.

The byte is the lowest level at which we can access data; there's no "bit" type, and we can't ask for
an individual bit. In fact, we can't even perform operations on a single bit -- every bitwise operator
will be applied to, at a minimum, an entire byte at a time. This means we'll be considering the whole
representation of a number whenever we talk about applying a bitwise operator.

Left & Right shift operator

The leftshift operator is the equivalent of moving all the bits of a number a specified number of
places to the left:

[variable]<<[number of places]

For instance, consider the number 8 written in binary 00001000. If we wanted to shift it to the left 2
places, we'd end up with 00100000; everything is moved to the left two places, and zeros are added
as padding. This is the number 32 -- in fact, left shifting is the equivalent of multiplying by a power of
two.

int mult_by_pow_2(int number, int power)


{
return number<<power;
}
Note that in this example, we're using integers, which are either 2 or 4 bytes, and that the operation
gets applied to the entire sequence of 16 or 32 bits.

But what happens if we shift a number like 128 and we're only storing it in a single byte: 10000000?
Well, 128 * 2 = 256, and we can't even store a number that big in a byte, so it shouldn't be surprising
that the result is 00000000.

It shouldn't surprise you that there's a corresponding right-shift operator: >>. Note that a bitwise
right-shift will be the equivalent of integer division by 2.

Why is it integer division? Consider the number 5, in binary, 00000101. 5/2 is 2.5, but if you are
performing integer division, 5/2 is 2. When you perform a right shift by one: (unsigned int)5>>1, you
end up with 00000010, as the rightmost 1 gets shifted off the end; this is the representation of the
number 2. Note that this only holds true for unsigned integers; otherwise, we are not guaranteed
that the padding bits will be all 0s.

Generally, using the left and right shift operators will result in significantly faster code than

2013 Page 5
Ankur Rastogi Unit-V C Pre Processor & Bitwise Operator

calculating and then multiplying by a power of two. The shift operators will also be useful later when
we look at how to manipulating individual bits.

For now, let's look at some of the other binary operators to see what they can do for us.

Operator Description Example

Binary AND Operator copies a bit to the result if it (A & B) will give 12 which is 0000
&
exists in both operands. 1100

Binary OR Operator copies a bit if it exists in either (A | B) will give 61 which is 0011
|
operand. 1101

Binary XOR Operator copies the bit if it is set in one (A ^ B) will give 49 which is 0011
^
operand but not both. 0001

Binary Ones Complement Operator is unary and has (~A ) will give -60 which is 1100
~
the effect of 'flipping' bits. 0011

Binary Left Shift Operator. The left operands value is


A << 2 will give 240 which is 1111
<< moved left by the number of bits specified by the
0000
right operand.

Binary Right Shift Operator. The left operands value is


A >> 2 will give 15 which is 0000
>> moved right by the number of bits specified by the
1111
right operand.

Bitwise AND
The bitwise AND operator is a single ampersand: &. It works on smaller pieces (bits instead of bytes,
chars, integers, etc). A binary AND simply takes the logical AND of the bits in each position of a
number in binary form.

For instance, working with a byte (the char type):

01001000 &
10111000 =
--------
00001000

The most significant bit of the first number is 0, so we know the most significant bit of the result
must be 0; in the second most significant bit, the bit of second number is zero, so we have the same

2013 Page 6
Ankur Rastogi Unit-V C Pre Processor & Bitwise Operator

result. The only time where both bits are 1, which is the only time the result will be 1, is the fifth bit
from the left. Consequently,

72 & 184 = 8

Bitwise OR
Bitwise OR works almost exactly the same way as bitwise AND. The only difference is that only one
of the two bits needs to be a 1 for that position's bit in the result to be 1. (If both bits are a 1, the
result will also have a 1 in that position.) The symbol is a pipe: |. Again, this is similar to boolean
logical operator, which is ||.

01001000 |
10111000 =
--------
11111000
and consequently

72 | 184 = 248

The Bitwise Complement


The bitwise complement operator, the tilde, ~, flips every bit. A useful way to remember this is that
the tilde is sometimes called a twiddle, and the bitwise complement twiddles every bit: if you have a
1, it's a 0, and if you have a 0, it's a 1.

This turns out to be a great way of finding the largest possible value for an unsigned number:

unsigned int max = ~0;

0, of course, is all 0s: 00000000 00000000. Once we twiddle 0, we get all 1s: 11111111 11111111.
Since max is an unsigned int, we don't have to worry about sign bits or twos complement. We know
that all 1s is the largest possible number.

Note that ~ and ! cannot be used interchangeably. When you take the logical NOT of a non-zero
number, you get 0 (FALSE). However, when you twiddle a non-zero number, the only time you'll get
0 is when every bit is turned on.

Bitwise Exclusive-Or (XOR)


There is no boolean operator counterpart to bitwise exclusive-or, but there is a simple explanation.
The exclusive-or operation takes two inputs and returns a 1 if either one or the other of the inputs is
a 1, but not if both are. That is, if both inputs are 1 or both inputs are 0, it returns 0. Bitwise
exclusive-or, with the operator of a carrot, ^, performs the exclusive-or operation on each pair of
bits. Exclusive-or is commonly abbreviated XOR.

2013 Page 7
Ankur Rastogi Unit-V C Pre Processor & Bitwise Operator

For instance, if you have two numbers represented in binary as 10101010 and 01110010 then taking
the bitwise XOR results in 11011000. It's easier to see this if the bits are lined up correctly:

01110010 ^
10101010
--------
11011000

You can think of XOR in the following way: you have some bit, either 1 or 0, that we'll call A. When
you take A XOR 0, then you always get A back: if A is 1, you get 1, and if A is 0, you get 0. On the
other hand, when you take A XOR 1, you flip A. If A is 0, you get 1; if A is 1, you get 0.

#include <stdio.h>

main()
{

int a = 60; /* 60 = 0011 1100 */


int b = 13; /* 13 = 0000 1101 */
int c = 0;

c = a & b; /* 12 = 0000 1100 */


printf("Line 1 - Value of c is %d\n", c );

c = a | b; /* 61 = 0011 1101 */
printf("Line 2 - Value of c is %d\n", c );

c = a ^ b; /* 49 = 0011 0001 */
printf("Line 3 - Value of c is %d\n", c );

c = ~a; /*-61 = 1100 0011 */


printf("Line 4 - Value of c is %d\n", c );

c = a << 2; /* 240 = 1111 0000 */


printf("Line 5 - Value of c is %d\n", c );

c = a >> 2; /* 15 = 0000 1111 */


printf("Line 6 - Value of c is %d\n", c );
getch();
}

2013 Page 8

You might also like