Tuesday, November 1, 2011

Static Storage Class data types in C programming language

The features of a variable defined to have a static storage class are as under:
Storage
− Memory.
Default initial value
− Zero.
Scope
− Local to the block in which the variable is defined.
Life
− Value of the variable persists between different function calls.
Compare the two programs and their output given in Figure 6.3 to understand the difference between the automatic and static storage classes.






The programs above consist of two functions main( ) and increment( ). The function increment( ) gets called from main( ) thrice. Each time it increments the value of i and prints it. The only difference in the two programs is that one uses an auto storage class for variable i, whereas the other uses static storage class.Like auto variables, static variables are also local to the block in which they are declared. The difference between them is that static variables don’t disappear when the function is no longer active. Their values persist. If the control comes back to the same function again the static variables have the same values they had last time around.
In the above example, when variable i is auto, each time increment( ) is called it is re-initialized to one. When the function terminates, i vanishes and its new value of 2 is lost. The result: no matter how many times we call increment( ), i is initialized to 1 every time.
On the other hand, if i is static, it is initialized to 1 only once. It is never initialized again. During the first call to increment( ), i is incremented to 2. Because i is static, this value persists. The next time increment( ) is called, i is not re-initialized to 1; on the contrary its old value 2 is still available. This current value of i (i.e. 2) gets printed and then i = i + 1 adds 1 to i to get a value of 3. When increment( ) is called the third time, the current value of i (i.e. 3) gets printed and once again i is incremented. In short, if the storage class is static then the statement static int i = 1 is executed only once, irrespective of how many times the same function is called.
Consider one more program.
main( )
{
int *j ;
int * fun( ) ;
j = fun( ) ;
printf ( "\n%d", *j ) ;
}
int *fun( )
{
int k = 35 ;
return ( &k ) ;
}
Here we are returning an address of k from fun( ) and collecting it in j. Thus j becomes pointer to k. Then using this pointer we are printing the value of k. This correctly prints out 35. Now try calling any function (even printf( ) ) immediately after the call to fun( ). This time printf( ) prints a garbage value. Why does this happen? In the first case, when the control returned from fun( ) though k went dead it was still left on the stack. We then accessed this value using its address that was collected in j. But when we precede the call to printf( ) by a call to any other function, the stack is now changed, hence we get the garbage value. If we want to get the correct value each time then we must declare k as static. By doing this when the control returns from fun( ), k would not die.

All this having been said, a word of advice—avoid using static variables unless you really need them. Because their values are kept in memory when the variables are not active, which means they take up space in memory that could otherwise be used by other variables.

Register Storage Class of data types in C programming language

The features of a variable defined to be of register storage class are as under:
Storage
- CPU registers.
Default initial value
- Garbage value.
Scope
- Local to the block in which the variable is defined.
Life
- Till the control remains within the block in which the variable is defined.
A value stored in a CPU register can always be accessed faster than the one that is stored in memory. Therefore, if a variable is used at many places in a program it is better to declare its storage class as register. A good example of frequently used variables is loop counters. We can name their storage class as register.
main( )
{
register int i ;
for ( i = 1 ; i <= 10 ; i++ )
printf ( "\n%d", i ) ;
}
Here, even though we have declared the storage class of i as register, we cannot say for sure that the value of i would be stored in a CPU register. Why? Because the number of CPU registers are limited, and they may be busy doing some other task. What happens in such an event... the variable works as if its storage class is auto.
Not every type of variable can be stored in a CPU register.
For example, if the microprocessor has 16-bit registers then they cannot hold a float value or a double value, which require 4 and 8 bytes respectively. However, if you use the register storage class for a float or a double variable you won’t get any error messages. All that would happen is the compiler would treat the variables to be of auto storage class.

Automatic Storage Class in C programming language

The features of a variable defined to have an automatic storage class are as under:
Storage
− Memory.
Default initial value
− An unpredictable value, which is often called a garbage value.
Scope
− Local to the block in which the variable is defined.
Life
− Till the control remains within the block in which the variable is defined.
Following program shows how an automatic storage class variable is declared, and the fact that if the variable is not initialized it contains a garbage value.
main( )
{
auto int i, j ;
printf ( "\n%d %d", i, j ) ;
}
The output of the above program could be...
1211 221
where, 1211 and 221 are garbage values of i and j. When you run this program you may get different values, since garbage valuesare unpredictable. So always make it a point that you initialize the automatic variables properly, otherwise you are likely to get unexpected results. Note that the keyword for this storage class is auto, and not automatic.
Scope and life of an automatic variable is illustrated in the following program.
main( )
{
auto int i = 1 ;
{
{
{
printf ( "\n%d ", i ) ;
}
printf ( "%d ", i ) ;
}
printf ( "%d", i ) ;
}
}
The output of the above program is:
1 1 1
This is because, all printf( ) statements occur within the outermost block (a block is all statements enclosed within a pair of braces) in which i has been defined. It means the scope of i is local to the block in which it is defined. The moment the control comes out of the block in which the variable is defined, the variable and its value is irretrievably lost. To catch my point, go through the following program.
main( )
{
auto int i = 1 ;
{
auto int i = 2 ;
{
auto int i = 3 ;
printf ( "\n%d ", i ) ;
}
printf ( "%d ", i ) ;
}
printf ( "%d", i ) ;
}
The output of the above program would be:
3 2 1

Note that the Compiler treats the three i’s as totally different variables, since they are defined in different blocks. Once the control comes out of the innermost block the variable i with value 3 is lost, and hence the i in the second printf( ) refers to i with value 2. Similarly, when the control comes out of the next innermost block, the third printf( ) refers to the i with value 1.
Understand the concept of life and scope of an automatic storage class variable thoroughly before proceeding with the next storage class.

Floats and Doubles in C programming language

A float occupies four bytes in memory and can range from -3.4e38 to +3.4e38. If this is insufficient then C offers a double data type that occupies 8 bytes in memory and has a range from -1.7e308 to +1.7e308. A variable of type double can be declared as,
double a, population ;
If the situation demands usage of real numbers that lie even beyond the range offered by double data type, then there exists a long double that can range from -1.7e4932 to +1.7e4932. A long double occupies 10 bytes in memory.
You would see that most of the times in C programming one is required to use either chars or ints and cases where floats, doubles or long doubles would be used are indeed rare.
Let us now write a program that puts to use all the data types that we have learnt in this chapter. Go through the following program carefully, which shows how to use these different data types. Note the format specifiers used to input and output these data types.
main( )
{
char c ;
unsigned char d ;
int i ;
unsigned int j ;
short int k ;
unsigned short int l ;
long int m ;
unsigned long int n ;
float x ;
double y ;
long double z ;
/* char */
scanf ( "%c %c", &c, &d ) ;
printf ( "%c %c", c, d ) ;
/* int */
scanf ( "%d %u", &i, &j ) ;
printf ( "%d %u", i, j ) ;
/* short int */
scanf ( "%d %u", &k, &l ) ;
printf ( "%d %u", k, l ) ;
/* long int */
scanf ( "%ld %lu", &m, &n ) ;
printf ( "%ld %lu", m, n ) ;
/* float, double, long double */
scanf ( "%f %lf %Lf", &x, &y, &z ) ;
printf ( "%f %lf %Lf", x, y, z ) ;
Chapter
}

The essence of all the data types that we have learnt so far has been captured in Figure 6.2.


Storage Classes in C programming data types

We have already said all that needs to be said about constants, but we are not finished with variables. To fully define a variable one needs to mention not only its ‘type’ but also its ‘storage class’. In other words, not only do all variables have a data type, they also have a ‘storage class’.
We have not mentioned storage classes yet, though we have written several programs in C. We were able to get away with this because storage classes have defaults. If we don’t specify the storage class of a variable in its declaration, the compiler will assume a storage class depending on the context in which the variable is used. Thus, variables have certain default storage classes.
From C compiler’s point of view, a variable name identifies some physical location within the computer where the string of bits representing the variable’s value is stored. There are basically two kinds of locations in a computer where such a value may be kept— Memory and CPU registers. It is the variable’s storage class that determines in which of these two locations the value is stored.
Moreover, a variable’s storage class tells us:
Moreover, a variable’s storage class tells us:

(a) Where the variable would be stored.
(b) What will be the initial value of the variable, if initial value is not specifically assigned.(i.e. the default initial value).
(c) What is the scope of the variable; i.e. in which functions the value of the variable would be available.
(d) What is the life of the variable; i.e. how long would the variable exist.

There are four storage classes in C:
(a) Automatic storage class
(b) Register storage class
(c) Static storage class
(d) External storage class
Let us examine these storage classes one by one.

A Few More Issues of data types in C programming language

Having seen all the variations of the primary types let us take a look at some more related issues.
(a) We saw earlier that size of an integer is compiler dependent. This is even true in case of chars and floats. Also, depending upon the microprocessor for which the compiler targets its code the accuracy of floating point calculations may change. For example, the result of 22.0/7.0 would be reported moreaccurately by VC++ compiler as compared to TC/TC++ compilers. This is because TC/TC++ targets its compiled code to 8088/8086 (16-bit) microprocessors. Since these microprocessors do not offer floating point support, TC/TC++ performs all float operations using a software piece called Floating Point Emulator. This emulator has limitations and hence produces less accurate results. Also, this emulator becomes part of the EXE file, thereby increasing its size. In addition to this increased size there is a performance penalty since this bigger code would take more time to execute.

(b) If you look at ranges of chars and ints there seems to be one extra number on the negative side. This is because a negative number is always stored as 2’s compliment of its binary. For example, let us see how -128 is stored. Firstly, binary of 128 is calculated (10000000), then its 1’s compliment is obtained (01111111). A 1’s compliment is obtained by changing all 0s to 1s and 1s to 0s. Finally, 2’s compliment of this number, i.e. 10000000, gets stored. A 2’s compliment is obtained by adding 1 to the 1’s compliment. Thus, for -128, 10000000 gets stored. This is an 8-bit number and it can be easily accommodated in a char. As against this, +128 cannot be stored in a char because its binary 010000000 (left-most 0 is for positive sign) is a 9-bit number. However +127 can be stored as its binary 01111111 turns out to be a 8-bit number.

(c) What happens when we attempt to store +128 in a char? The first number on the negative side, i.e. -128 gets stored. This is because from the 9-bit binary of +128, 010000000, only the right-most 8 bits get stored. But when 10000000 is stored the left-most bit is 1 and it is treated as a sign bit. Thus the value of the number becomes -128 since it is indeed the binary of -128, as can be understood from (b) above. Similarly, you can verify that an attempt to store +129 in a char results in storing -127 in it. In general, if we exceed the range from positive side we end up on the negative side. Vice versa is also true. If we exceed the range from negative side we end up on positive side.

Chars, signed and unsigned in C programming language

Parallel to signed and unsigned ints (either short or long), similarly there also exist signed and unsigned chars, both occupying one byte each, but having different ranges. To begin with it might appear strange as to how a char can have a sign. Consider the statement
char ch = 'A' ;
Here what gets stored in ch is the binary equivalent of the ASCII value of ‘A’ (i.e. binary of 65). And if 65’s binary can be stored, then -54’s binary can also be stored (in a signed char).
A signed char is same as an ordinary char and has a range from -128 to +127; whereas, an unsigned char has a range from 0 to 255. Let us now see a program that illustrates this range:
main( )
{
char ch = 291 ;
printf ( "\n%d %c", ch, ch ) ;
}
What output do you expect from this program? Possibly, 291 and the character corresponding to it. Well, not really. Surprised? The reason is that ch has been defined as a char, and a char cannot take a value bigger than +127. Hence when value of ch exceeds +127, an appropriate value from the other side of the range is picked up and stored in ch. This value in our case happens to be 35, hence 35 and its corresponding character #, gets printed out.
Here is another program that would make the concept clearer.
main( )
{
char ch ;
for ( ch = 0 ; ch <= 255 ; ch++ )
printf ( "\n%d %c", ch, ch ) ; }
This program should output ASCII values and their corresponding characters. Well, No! This is an indefinite loop. The reason is that ch has been defined as a char, and a char cannot take values bigger than +127. Hence when value of ch is +127 and we perform ch++ it becomes -128 instead of +128. -128 is less than 255 hence the condition is still satisfied. Here onwards ch would take values like -127, -126, -125, .... -2, -1, 0, +1, +2, ... +127, -128, -127, etc. Thus the value of ch would keep oscillating between -128 to +127, thereby ensuring that the loop never gets terminated. How do youovercome this difficulty? Would declaring ch as an unsigned char solve the problem? Even this would not serve the purpose since when ch reaches a value 255, ch++ would try to make it 256 which cannot be stored in an unsigned char. Thus the only alternative is to declare ch as an int. However, if we are bent upon writing the program using unsigned char, it can be done as shown below. The program is definitely less elegant, but workable all the same.
main( )
{
unsigned char ch ;
for ( ch = 0 ; ch <= 254 ; ch++ )
printf ( "\n%d %c", ch, ch ) ;
printf ( "\n%d %c", ch, ch ) ;
}