Useful Classes and Functions - IV
The following topics are covered in this section:
First of all, there isn’t any international standard for the purpose of writing C++ programs. You are free to write the way you want to. This section is something like a section of tips that you could use.
x = 3+4*10/5+1-4/2;
Writing something like this might make one feel that the person who wrote this was a genius! Avoid such statements (even the programmer himself would have to break his head before he is certain of the answer). Use simple brackets to break the long calculations into smaller parts as shown below:
( 3 + ( 4 * (10/5) ) + 1 - (4/2) );
Now, anyone will be able to say that the result of the above statement is 10. See how a few brackets can make a big difference!
( 3 + ( 4 * (10/5) ) + 1 - (4/2) ); is better than
(3+(4*(10/5))+1-(4/2));
for (i =0; i<10 ; i++)
{
for (int j = 0 ; j<10 ; j++)
{
cout<<i*j;
}
}
{
for (int i = 0 ; i<5 ; i++)
cout<<endl<<i;
return 0;
}
will print the output as:
0
1
2
3
4
Check out the modification of the above code. Here we want to print the number and also its square.
int main( )
{
for (i = 0 ; i<5 ; i++)
cout<<endl<<i;
cout<<endl<<i*i;
return 0;
}
will give the output as:
0
1
2
3
4
25
The statement i*i is not considered as part of the ‘for’ loop itself. This problem can be avoided if you clearly specify the ‘for’ block.
int main( )
{
for (int i=0;i<5;i++)
{
cout<<endl<<i;
cout<<endl<<i*i;
}
return 0;
}
Now there is no confusion about what is included in the ‘for’ block.
cout<<"This is a really really really"
" long line that i am typing";
for (int j=0; j<=strlen(str);j++)
{
}
This forces the strlen ( ) function to be invoked for every iteration (every function call involves a lot of overhead). You can rewrite the above code as:
int len=strlen(str);
for (int j=0; j<=len;j++)
{
}
Now the strlen( ) function is invoked only once.
Typedef basically stands for type definition. The keyword ‘typedef’ lets you create your own data types; well, not exactly your own data types but your own names for the standard data types. Consider the following:
int x;
means that x is declared to be an integer variable. Now, using typedef you can give a different name for ‘int’. The syntax for using different names instead of the standard ones is:
typedef standard-name new-name;
For example:
typedef int weight;
weight x=50;
In this case, we are using the name of ‘weight’ instead of the standard name ‘int’. Thus you can now declare your variables by using ‘weight’ instead of ‘int’. Basically we are just giving ‘int’ a new name called ‘weight’.
Remember: By using typedef we are only giving a new name for an existing standard data type. We are not creating a new data type.
This section will explain another set of operators. Bit-wise operators will act on binary digits. Maybe you know that C programming supports assembly level programming. In assembly level programming there is a definite necessity for operating on bits (mind you, not decimal numbers but binary digits). There might be other applications where you would like to manipulate data at the bit level. For example you could use it for hiding data within images (this is called steganography). You can make some bit level manipulations in your image file (such that the image appears to be the same) and hide data within the file.
Basically if you want to operate on the individual bits instead of operating on the number as a decimal number, then you should use bitwise operators. Bit-wise operators can also be used to perform some calculations faster or with lesser number of steps. The bit operators are:
If you’ve done a course in digital electronics you will know about the AND gate, OR gate, EXOR gate and NOT gate. These operators are exactly similar to these logic gates. Let us assume that ‘x’ and ‘y’ are two separate bits (they are binary digits not decimal numbers). The effect of each operation on these bits is shown below:
x |
y |
x & y |
0 |
0 |
0 |
0 |
1 |
0 |
1 |
0 |
0 |
1 |
1 |
1 |
AND Operation
The AND operator will give a result of 1, only if both ‘x’ and ‘y’ are equal to 1. Similarly, the OR operator will give a resultant value of 1 if ‘x’ or ‘y’ value is 1.
EX-OR (stands for exclusively OR). This operator will yield a value of 1 only if ‘x’ alone is 1 or if ‘y’ alone is 1.
The NOT operator is also known as the complementary operator. If you give an input of 1, the output will be 0 and vice-versa.
x |
y |
x | y (x OR y) |
0 |
0 |
0 |
0 |
1 |
1 |
1 |
0 |
1 |
1 |
1 |
1 |
OR Operation
x |
y |
x ^ y (x EXOR y) |
0 |
0 |
0 |
0 |
1 |
1 |
1 |
0 |
1 |
1 |
1 |
0 |
EX-OR Operation
x |
~x (NOT x) |
0 |
1 |
1 |
0 |
NOT Operation
First of all, you should have an idea about how these operations will work on a group of binary digits. Eight bits make up a byte and we have already seen the process of conversion from decimal to binary format in the first chapter itself.
Let us assume that we have two numbers: 128 and 255. Their binary representations will be: 0100 0000 and 1000 0000.
0 1 0 0 0 0 0 0 When you perfom bitwise AND operation, each pair of bits
& 1 0 0 0 0 0 0 0 are ANDed individually
------------------
0 0 0 0 0 0 0 0 Resultant value (0)
0 1 0 0 0 0 0 0 OR operation performed
| 1 0 0 0 0 0 0 0
------------------
1 1 0 0 0 0 0 0 Resultant value (192)
0 1 0 0 0 0 0 0 EXOR operation performed
^ 1 0 0 0 0 0 0 0
------------------
1 1 0 0 0 0 0 0 Resultant value (192)
0 1 0 0 0 0 0 0 NOT operation performed on decimal value of 64
~ ------------------
1 0 1 1 1 1 1 1 Resultant value (191)
If you apply the tabulation that was given, you will get the results shown above. We can do obtain the same result using a C++ program.
int main( )
{
unsigned char x,y;
x=128;
y=64;
cout<<endl<<"x AND y = "<< (x&y);
cout<<endl<<"x OR y = "<< (x|y);
cout<<endl<<"x EXOR y = "<< (x^y);
return 0;
}
The output will be:
x AND y = 0
x OR y = 192
x EXOR y = 192
There are two more bit-oriented operators. They are called the shifting operators:
Their syntax is:
operand << number-of-bits-to-shift
operand >> number-of-bits-to-shift
Let us consider the two operations with the help of a variable ‘x’.
Let the value of ‘x’ be 96 (and let us assume that the representation is unsigned representation). Therefore in binary form x = 0 1 1 0 0 0 0 0
0 1 1 0 0 0 0 0 >> 1 (means shifting right by one bit will give)
= 0 0 1 1 0 0 0 0 (equivalent decimal value of 48)
0 1 1 0 0 0 0 0 >> 2 (means shifting right by two bits will give)
= 0 0 0 1 1 0 0 0 (equivalent decimal value of 24)
This is the right shift operation. As you can see, once the bits are shifted their original place is filled by a zero. Remember that we are assuming unsigned representation (for signed representation there is a slight difference that we shall see later).
The same logic applies for left shift operation as well.
0 1 1 0 0 0 0 0 << 1 (means shifting left by one bit will give)
= 1 1 0 0 0 0 0 0 (equivalent decimal value of 192)
0 1 1 0 0 0 0 0 << 2 (means shifting left by two bits will give)
= 1 0 0 0 0 0 0 0 (equivalent decimal value of 128)
In the second case of left shifting, we have ended up losing one of the bits. This is because the shifting operation is not a rotation operation. Once you shift a bit left beyond the 7th position (i.e. the MSB) or to the right beyond the 0th position (i.e. the LSB) you will lose the bit.
Beware: Shifting past the 7th bit to the left will lead to loss of the bit if the result is being stored in a ‘char’ (because a character variable can take up a maximum of 1 byte only). Suppose we have an integer variable named ‘z’ then it will occupy 4 bytes (or 2 bytes depending on the OS). If we store the value of 96 in z, it will be stored as shown below:
32 bits for z
00000000 00000000 00000000 01100000
This clearly indicates that you can shift the bits to the left by more than one place.
Another interesting point to note is that when we right shift by one bit, we are in effect dividing the number by 2 (48 = 96/2 and 24 = 48/2). Similarly when we left shift by one bit it is equivalent to multiplying the number by 2 (192 = 2 * 96).
For signed numbers there is a possibility for a slight modification. Signed numbers were handled in the first chapter. In signed numbers, the 7th bit is used to denote the sign (1 for negative and 0 for positive). When you perform shifting operations on signed numbers, usually the vacant spaces will be filled up by the sign bit. In some compilers it may be filled up by 0 always irrespective of whether the number is signed or unsigned. So be careful while performing bit operations on signed numbers. By the way, the sign bit is the MSB of the binary number (if it an 8-bit number then the 7th bit is the sign bit. If it is a 16-bit number then the 15th bit is the sign bit. For easier explanation we have considered the case of an 8-bit number).
During shifting it is not that only the 1s move. The 0s and 1s will be shifted together.
0 0 1 0 1 0 0 0 << 2
= 1 0 1 0 0 0 0 0 Result (bold indicates the original bits)
In C++, for shifting the coding will be:
unsigned char y;
y=96;
cout<<endl<<"Shifting left by 1 = "<<(y<<1);
cout<<endl<<"Shifting right by 1 = "<<(y>>1);
Go back to the Contents Page 2
Copyright © 2004 Sethu Subramanian All rights reserved.