The Journey Begins ….

 Look at any successful individual and/or enterprise. If you try to trace that one common strand that runs through all these success stories you realize that there is tons of passion, almost a kind of mania, that drive such individuals to success. They put their heart and soul (and many times their savings) into the task at hand. That we believe is the only way to sure success. Brian Kernighan and Dennis Richie were two such individuals. They had written an operating system (a computer program). It worked just fine, except for one small irritant-- their program ran only on one machine. They looked for solutions but couldn't find one. They believed that when you cannot find a solution, create one. They decided to write a programming language so that the OS would run different machines.(Mac, PC’s..).This was the guiding philosophy behind the birth of C.

 The good thing about C, is that it has been written for programmers who understand the needs, requirements and expectations of fellow programmers. So, it cuts through the shackles of conventional programming languages and gives you, the programmer, more control. The immense success of C can be attributed to the fact that C talks to the computer one-on-one. No wonder then that C is often termed a "hardcore" programming language.

 Take the plunge

 We are not going to tell you how to run the first C program because Borland C does it in one way, Microsoft C does it in some other way. My advice to you is to go ahead and take the plunge and type this first C program. Type:

 program 1

main()
{
printf("Hello")
}
 

Now if you are using BorlandC, say Alt-B, B. You will get an error. Go ahead and compare your code with what we have given. If you are already getting frustrated we suggest you buy a strip of "anti-frustration tablets" right away because you will need loads of them while learning C. While you are munching on those tablets read this mythological tale.

 According to an old jungle saying about C, if you get your first C program running in the first attempt, the gods of C connive against you to ensure that all your future programs are error -ridden. Hence in compliance with the wishes of the C gods, we made sure our first program didn't run. And if you didn't get an error it means two things: one, that you are not following our code exactly (which is not a very smart idea, as you will find in due course) and second that you have already been black listed in the book of records of the gods of C, which is certainly not an auspicious start.

 Let us look at each word of the program and understand. The first line says:

 main()

 Whenever C sees any word with an open and close brace ‘()’, it recognizes the word as a function name. Which should tell you that printf is also understood by C as a function. Within the printf function, whatever is enclosed withim double quotes (" ") is written to the screen. In every programming language there has to a starting point for the code, an entry point from where the compiler knows it has to start compiling. For instance in Dbase the first line of the code is the starting point. In C main() is the starting point of your code. Hence even if main() is not written at the very first line of your code, the C compiler will find it and start executing your program from that point. main() and printf() are both functions, but main() is the first function to be called by the C compiler. A function could display for you just a Hi or it could contain 3 trillion lines of code. What the function is or what it does, we will understand later. For now it is sufficient to say that main() and printf() are both functions where main() is the first function to be called.

Another difference between main() and printf() is that when we say main() we are creating a function main(), when we say printf() we are calling the function printf(). This can be inferred since the word main() has no semicolon (;) following it, while printf has a semicolon at the end. What happens to printf() before or after it is called should not bother you.(unless you want to incur the wrath of the C gods.)

Now that we have created a function main() we need a way to tell the C compiler where it begins and ends. This could be done my using words like Start and End. Here, Kernighan and Richie had a tryst with destiny. One night while they were working, hunger struck. K grabbed a packet of instant noodles. One small piece of this noodle feel on their work book and left a ‘{‘ on the paper. Kernighan and Richie regarded this as a message from heaven and decided to use ‘{‘ to indicate the start of a function and '}' to indicate the end. What we are saying is that this is a part of the syntax of C and when it comes to syntax, we cannot argue. If it is the syntax it has to be followed. Get it? Got it! Good!!

You might now be getting desperate to get your first C program running. Type the following code and we assure you by all the stars in the sky that you will see the correct output :

program 2

main()
{
printf("Hello");
}

Output

Hello

 Notice the change? If you have and are mumbling : " But when we see the ‘)’ of printf is’int it obvious that the function has ended? Why do we need a semicolon(;)?". You don’t realize that you belong to the elite group of gifted people who can see through problems. But the C compiler is dumb and needs a semicolon (;) to know that the end of a statement is reached. Give C the semicolon and you will only make your own life peaceful.

Kenighan and Richie didn't like anything visual. In program 2, you have put pressed a carriage return (Enter, in common parlance) at the end of each statement. But the C compiler couldn't care less. It just disregards the spaces which brings us to our next example :

program3

main() { printf("Hell") ;}

Output

Hell

 The output of program 3 will be identical to that program 2 except that now you see Hell instead of Hello. (We have chosen to display Hell, not because we are possessed or haunted, but because we know precisely what you are going through at the moment).

 Lets look at the next example :

 program4

main()
{
printf("HelloWorld");
printf("GoodBye");
printf("No");
}
 

You have tried your best to display the output on 3 different lines by using 3 different printf’s. But the C compiler does not understand your intentions and goes ahead and displays :

 HelloWorldGoodByeNo

C remembers where it printed last (how it remembers is not important). So, it will continue printing at the place it last printed unless you write ‘\n’. This is called a new line tag. Type the following program and you will understand better :

program5

main()
{
printf("hello\nworld");
printf("\n\nhi");
printf("goodbye");
}

Output

hello
world

higoodbye

 

Each ‘\n’ that C encounters, it will keep jumping to the start of a new line.

Look at this program:

program6

main()
{
printf("hello %d\n",50);
}

In India, we have one particular number that we could have done without, the number 420. Now we as Indians know it means a crook, swindler....! But call an American a 420, he won’t know what you are talking about. ‘%d’ doesn't make any sense to us but when C sees a '%d' it replaces it with a number.

Thus, when C encounters:

printf("hello %d\n",50);

it replaces %d with 50.

Output

hello 50

 

program 7

main()
{
printf("%d..%d\n",50,100);
}

Now ask yourself: "Is their any unholy relation between the 2 dots separating %d and the fact that we are displaying 2 numbers". Now tell yourself "How can I be so silly?" Use any number of dots, it doesn't matter. If you think 5 dots in the output will make your make your day, go ahead use them.

Output

50..100

 program 8

main()
{
int i;
i=10;
printf("%d\n",i);
i=12;
printf("%d\n",i);
i=i+3;
printf("%d\n",i);
i=i+1;
printf("%d\n",i);
i++;
printf("%d\n",i);
}

Never be perturbed by a longish program. We'll explain each statement step by step. Let us first understand what we mean by

int i;

When you say int i we are declaring i as a variable. What we are doing is reserving some location in memory and calling it i. This means that i is a name of a memory location that is used to store integer numbers(hence the int). Notice that the color of int changes which indicates that C understands the word int. We initialize the value of i to 10. In the fifth line of code we change the value of i to 12. Aha! What does this mean? i was 10 initially. Now it has become 12. This means that i is a variable, because its value varies.(Ufff ... how profound). Pictorially, you can imagine it this way :

Then we say:

i=i+3;

This statement should tell you why variables are such a handy thing. Always look at the right hand side of the equal to sign. What we are telling the C compiler is to make add 3 to the current value of i. Thus we are effectively saying:

i=12+3

 which makes i 15 and then increment (increase value by 1) again, and again. The output will look like this:

10
12
15
16
17
 

Notice that i=i+1 and i++ do the same thing. They both increment the value of i by 1. Then why two different ways of doing the same thing?? i++ can be regarded as a short form for i=i+1. There is yet another way of denoting an increment in i, but i wont tell you about it. My problem with these short forms is: they mean more work, more to learn and remember and hence more the chances of error. People regard this as a great feature of C. Personally, we think all this is pretty silly. Keep things as standard as possible, is what we believe in.

We have to do some ground work before we get to the next program.

Take a program in some programming language like Dbase and run it. It will take some time to execute, lets say 10 secs. Now we take this program and submit it to a ‘Dbase to C converter’. Now the original program, without changes has been converted into a C understandable program. Run this program. It will need 1 sec to run. Which brings us to a very important aspect of computing -- speed. Modern computers are supposed to be fast. The moment the computer is asked to access its memory, its speed drops substantially. Till the time the computer does not look at memory, its speed can be pretty amazing.

In any computer, you cannot store a number less than 0 or greater than 255 in a single memory location. This rule is ubiquitous. So when you declare i as a variable and say i=10, you are storing the value 10 in some memory location which you have named i. Now suppose you were to store the value 300 into the computers memory, how would you do it? This value (300) cannot be fitted in a single memory location. Two memory locations will now be needed. We’ll delve into memory management later but it would suffice to say that we can store 2^16 or 655536 numbers in 2 memory locations. In the same way numbers as large as 4 billion (2^24) can be stored in 4 memory locations. But, we need to ask ourselves a simple question: if I need to store a variable whose value is 10, and I have the option of storing them in either 1,2 or 4 memory locations, which of these would result in the fastest retrieval?’. Dbase allocates the maximum memory to each variable. C, however is very stingy about memory. Very stingy!! (and rightfully so, since allocating more memory than required not only slows the computer but also amounts to memory wastage .. and remember what mama said :’Wastage of any kind is wrong’!!)

When a variable is created C will ask you how much memory would be required by the variable. You will have to answer this question. Instead of answering in a cryptic fashion, we declare the variable as either a char, an int or a long. Each of these occupy a different size of computer memory. Don’t believe me? Try this program :

Program 9

main()
{
int i;
char j;
long k;
printf("%d\n", sizeof(i));
printf("%d..%d\n", sizeof(j), sizeof(k));
}

Output

2
1..4

 Now we are going to change gear. Look at the following program :

 Program 10

main()
{
if(0)
printf("hi\n");
printf("bye");
}
 

if is a word understood by C, which is obvious since the color changes as you type. if is a decision making tool in C. we have said :

 if(0)

When the compiler sees a 0, it understands it as False. Hence this statement simply instructs the C compiler to ignore the next statement. Hence the output is:

bye

 Try guessing the output of this one:

main()
{
if(0)
{
printf("hi\n");
printf("bye");
}
}

If you get a blank screen, don’t bang your monitor in disgust. In fact, your program is working just fine. Notice that you have enclosed both the printf's within the if statement, which causes both statements to be comfortably ignored! Hence the blank screen! Simple!!

Try this program:

Program 11

main()
{
if(3)
printf("hi\n");
printf("bye");
}

whenever we have any number, other than 0 in the if condition, the compiler understands it as true and does not ignore the following statement. hence the output here is:

hi
bye

Similarly:

Program 12

main()
{
int i;
i=0;
if (i)
printf("hi\n");
}

we have already explain to you that we can always specify a variable name instead of a number. You will get a blank screen as output.

Still at variables and the if statement try this program:

Program 13

main()
{
int i;
i=0;
if (i)
printf("hi\n");
i=6;
if(i)
printf("bye\n");
}

Here initially i is 0, hence Hi will not be printed, but then we have changed the value of i to 6. Now the statement following the if statement will not be ignored and hence we see

Bye

Operators

Doctors operate on (poor) patients. Operators operate on numbers. Example of operators are: =,<=,>=,!=,>,<.

Now look at this program:

Program 14

main()
{
printf("%d",4>2);
}

Here C will play a game of "Yes" and "No" with you and print out 1 when it thinks the condition is true and 0 when it thinks the condition is false. So the output will be:

1

Try this program:

Program 15

main()
{
printf("%d",4<2);
}

Continuing in the same vein, the output will be 0. (False)

Lets play this game once more to understand completely:

Program 16

main()
{
printf("%d",3>=3);
printf("%d",3!=3);
printf("%d",3!=4);
printf("%d",!0);
printf("%d",!3);
printf("%d",3==4);
printf("%d",3==3);
}

In the second printf we have said:

printf("%d",3!=3);

The != stands for not equal to. Thus here we are saying 3!=3, which is false, hence the output is 0.

In the sixth printf wehave:

printf("%d",3==4)

Whenever C sees a double equal to, it asks a question. ‘Is 3 equal to 4?’ The answer is no and hence the printf prints 0.

The output looks like this:

1011001

 Actually we have been a little unfair to you. We should have told you

 

 printf("%d",!0);

 

Whenever C compiler sees a ‘!’ it will invert the value. Which means:

!0 will return 1

!(any other number) will return 0

You are now about to arm yourself with another weapon from the C artillery. Type this program:

Program 17

main()
{
for(i=1;i<=10;i++)
printf("%d\n",i);
}

Here we have shown the for loop. The for loop takes 3 things each seperated by semicolons. But everything is a thing hence we call them paramaters. Hence we have 3 parameters and 2 semicolons. The first parameter establishes the initial value of i (in our case 0). The second parameter is the condition testing parameter. The third parameter is the action to be taken till the limiting condition is reached. The way the for loop works is:

first the varibale is initialized with the first parameter. Then the condition in the second paramater is tested. If the condition is true the instruction within the for loop are executed. At this time, the action specified in the third paramater is taken. Then the whole cycle is repeated.

Here, initially i=1. C checks if i is <=10. i.e. if 1<=10. Yes it is! It goes to

printf("%d\n",i);

 and prints 1

 Hence the output is 1. i is now incremented by the i++ part of the for loop. again C checks. is i<=10 i.e. is 2<=10. Yes it is!! It goes to:

 printf("%d\n",i); and prints 2

This goes on till the condition i<=10 is met. So the output is:

1
2
3
4
5
6
7
8
9
10

When i becomes 10, it is incremented once more. i now becomes 11. Now the condition i<=10 no longer hold true and we exit out of the for loop.

Make this small change to the code:

Program 18

main()
{
int i;
for(i=1;i<=10;i++)
printf("%d\n",i);
printf("%d\n",i);
}

Here the effect of the for loop is limited only to the first printf statement. (Since the semicolon(;) at the end of the first printf marks the end of the for loop). When we come out of the for loop the value of i is 11, which will be printed by the second printf statement. The output is:

1
2
3
4
5
6
7
8
9
10
11

One more small modification to the above program:

Program 19

main()
{
int i;
for(i=1;i<=10;i++)
{
printf("%d",i);
printf("%d",i);
}
}

Here the both the printf’s are within the for loop. ( This is because of ‘{‘ and ‘}’). This time around the output will be:

1122334455667788991010

 Next lets look at the next tool for looping: the while loop.

Program 20

main()
{
int i;
i=1;
while(i<=10)
{
printf("%d",i);
i++;
}
}

while takes just one parameter and that is the condition testing parameter. As long as that condition holds true, looping continues. Every time before entering the loop the condition is checked. If it holds true, the loop is executed. Here I is initialized to 1. Then the condition is tested. Is i<=10. i.e. is 1<=10. Yes it is!! Enter the loop. Here i is printed i.e. 1 is printed. i is then incremented. I now becomes 2. Again the condition i<=10 is checked. Is 2<=10. Yes it is!! Enter the loop again and print the value of i i.e. 2. This

goes on until the condition becomes false.

Output

12345678910

 Look at this code and try to figure out what will happen:

 Program 21

main()
{
int i;
i=1;
while(i<=10)
{
printf("%d\n",i);
}
}
 

Here we have not included i++ in the loop. this means that the value of i will not be incremented, i will always be at its initial value (which is 1), the condition i<=10 (i.e. 1<=10) will always be true and hence we will now have an infinite loop. So if you keep waiting for this loop to terminate you will ensure that you will miss the last train back home. Hence press Ctrl-Break to come out of the infinite loop.

Intelligent decision making requires the if statement to behave like a dictator. The if statement has a very simple philosophy in life: only if the condition specified in the brackets of the if statement is true will it execute the statements following it.

Program 22

main()
{
int i;
for(i=1;i<=10;i++)
{
if(i>=3)
printf("%d",i);
}
}

Here i starts of with the initial value of 0. Then the if condition (Is i>=3) is tested. Only when this condition is satisfied (i.e. only when this condition is true) will the value of i be printed. Hence the output of the program will look like this:

345678910

In many instances we would like to make the if statement more restrictive(in other words, making the dictatorship more effective). Look at this program:

 

Program 23

main()
{
int i;
for(i=1;i<=10;i++)
{
if(i>=3 && i<=7)
printf("%d\n",i);
}
}

&& should be read as 'And'. This is the reason why C is sometimes termed cryptic because a programming language like Dbase would have probably use the word 'and'

instead of '&&'. But given the speed and power of C, I feel it’s a small price to pay. This if statement, in English means only if both conditions (I>=3 and I<=7), print the value of i.

Output

3
4
5
6
7

Now let us take a closer look at the if statement. Type this program:

Program 24

main()
{
int i;
for(i=1;i<=10;i++)
{
if(i>=4)
printf("%d..\n",i);
else
printf("%d\n",i);
}
}

Here we have an if … else construct. Whenever you see an if … else, you should tell yourself mentally that either one of them (if or else) will have to be executed. So if the if condition is false, the else part will have to be true and vice-versa. Make this very clear in your mind as it will help you in future programming examples. Thus, in the above program, i will start with the initial value of 1. The if condition is :

if (i>=4)

Till the time i=3 this condition will not be true and hence the else part will automatically become true. The output will look like this:

1
2
3
4..
5..
6..
7..
8..
9..
10..

The important thing to keep in mind here is the first three numbers in the output (1 2 3) are printed by the printf statement in the else part and the rest of the numbers are printed by the printf statement in the if part of the program.

Now type this example:

Program 25

main()
{
int i;
i=4;
printf("%d\n",i=6);
printf("%d\n",i);
i=8;
printf("%d\n",i==7);
printf("%d\n",i);
}

Initially i has a value 4. Now in the first printf the value of i is changed to 6. Hence the output of the first printf will be 6. Note that assignment statements (like i=6) can be written any where in the code. And since within the printf we have said %d, this %d will be replaced by 6. The second printf will also display 6, since there has been no change in the value of i between the first and second printf. Then once again we are setting the value of i to 8. The third printf statement is interesting. Whenever C sees a double equal to sign(==), it knows it has to ask a question. In the third printf the question being asked is:' Is i (which is now 8) equal to 7?' The answer is 'No' or 'False' which is represented by 0, hence the third printf displays a 0. And since there has been no change in the value of i between the third and fourth printf, the fourth printf also displays 0.

Output

6
6
0
8

Look at this program:

Program 26

main()
{
int i;
for(i=0;i<=10;i++)
{
if(i=4)
printf("%d\n",i);
}
}
 

Here again, remember that the assignment statement can be put anywhere in the code. Here i has the initial value 0. When it comes to the if statement, the value of i first becomes 4 (due to the I=4). Then the if statement becomes:

if(i)

Which is always true and hence the value 4 is printed. Now i is incremented by 1, becomes 5. But once again when it comes to the if statement its value becomes 4 again, 4 gets printed and this goes on forever, since the value of I never reaches 10, and the for loop is never terminated. Thus, you should now act smart and break the infinite loop. (Ctrl-Break).

A very small modification to the above program will give you a very different output.

Program 27

main()
{
int i;
for(i=0;i<=10;i++)
{
if(i==4)
printf("%d\n",i);
}
}

Here in the if statement we ask: 'Is i equal to 4. This condition is satisfied only once as the value of I goes from 0 to 10 and hence the output will be

4

In the next program we will look at the concept of operator precedence. Operator precedence means a set of rules which dictate which opertaion will be performed first, when C finds two operators in the same statement. This program will tell you more:

Program 28

main()
{
int i;
i=6;
printf("%d\n",i=7!=8);
printf("%d\n",i);
}

Initially i=6. Now in the first printf we have the statement

 

i=7!=8

Whenever C finds 2 operators in the same line, it stops and ponders. It then processes the statement according to the rules of operator precedence. Just because the equal to (=) comes first in the statement, it doesn’t mean it will be executed first. According to this rule whenever C sees a not equal to (!=) and a equal to (=) in the same line, it executes the != part of the statement first. (the rest of the rules of operator precedence will be covered later). So the above statement is broken as :

7!=8

 which is true hence the value returned is 1. Then we have

 i=1

 Thus the first printf displays 1 and ditto for the second printf.

Output

 

1
1
 

Lets play with this same program:

Program 29

main()
{
int i;
i=6;
printf("%d\n",(i=7)!=8);
printf("%d\n",i);
}

The only change here is the parenthesis. Lets look at the following statement closely:

i=6;
(i=7)!=8;

Whenever C encounters a parenthesis it will execute the statement within the parenthesis first. Here initially i has the value 6. Then in the first printf, the value of i is changed to 7, because the statement:

i=7

 is executed first. Then the statement becomes:

 7!=8

 which is true, hence 1 and thus 1 gets printed by the first printf.

 The second printf will print the value of i which is now 7, since we have not touched the value of i. Hence the output will be:

1
7
 

The point to remember here is that whenever C encounters 2 or more operators in a single statement it will first execute that part of the statement which is in parenthesis because in the rules of operator precedence the parenthesis has the highest priority.

If you have followed our examples of precedence so far, you should be able to guess the output of this program:

Program 30

main()
{
int i;
i=6;
printf("%d\n",i=8!=8);
printf("%d\n",i);
}

Here C looks as 8!=8, which is false. Thus I becomes 0 and thus the output looks like this:

0
0

Now, we'll look at something new. Type this program:

Program 31

main()
{
printf("%d\n",65);
printf("%c\n",65);
}

Alright, time to tell you about a funny little rule: Computers all over the world, big or small understand only numbers. Letters of the alphabet do not make sense to these machines. We have told you what '%d' does. On similar lines, '%c', is used to display the character equivalent of a number. character equivalent of a number. Geek!! What's that??? All characters, alphabets, special characters, numbers are stored in the computer as numbers, i.e. all these have values associated with them. These are called the ASCII values. All these ASCII values are collective stored in a place called the ASCII table. The ASCII value of capital A('A') is 65. Why 65?? No answer. All characters have a value, A happens to be 65. So when we say:

printf("%c\n", 65);

 C peeps into the ASCII table and displays the character associated with ASCII value 65(which happens to be A.)

 Output

65
A
 

Try this example:

 Program 32

main()
{
printf("%c\n",66);
printf("%c\n",67);
printf("%c\n",97);
printf("%c\n",98);
printf("%c\n",99);
printf("%c\n",48);
printf("%c\n",49);
printf("%c\n",50);
printf("%c\n",0);
printf("%c\n",1);
printf("%c\n",2);
}

The output will be:

B
C
a
b
c
0
1
2

Note that the ASCII value of 0 is 48, that of 1 is 49 …! But 0 is the ASCII value of some other character. Don't get confused!

If you have followed our 'discourse' about ASCII values this next program should be a breeze.

Program 33

main()
{
int i;
i='A';
printf("%d..%c\n",i,i);
i='0';
printf("%d..%c\n",i,i);
i='1';
printf("%d..%c\n",i,i);
}

Output

65..A
48..0
49..1

Don't get confused when you see a '%c'. If you think %c should display only a alphabet, you are mistaken. %c is used to represent character equivalent of an ASCII value.

If you are dying to know all the ASCII values type this next program and populate your screen with all the 255 characters and their corresponding ASCII values.

Program 34

main()
{
int i;
for(i=0;i<=255;i++)
printf("%d..%c",i,i);
}

Output

Screen Full of Characters. Check it out for yourself!!

Level II

We feel like giving you a fancy opening paragraph welcoming you to Phase 2 of the Course, but we realize that it is in your best interest if we do not slack and keep the tempo going. So, try this program:

Program 35

main()
{
int *i;
char *j;
long *k;
printf("%d..%d..%d",sizeof(i),sizeof(j),sizeof(k));
}

This program is identical to program 9, except one slight change. All the variables now have a * in front of them. We have not done this to decorate our program. Whenever you see variables declared in such a manner, regard them as "special variables". (Kowtow to them, if you will). These variables are special because if you now run this program you will see that irrespective of the data type (int, char or long), the size of these variables in memory always remains 4. The output will look like this:

 

4..4..4

These special variables are given a special name. They are called pointers. Let me read your mind. ' Pointers!!!!. Gosh, I have heard they are the craziest and the most complex tings on the face of the earth. They have the capability of doing some weird things to the program. They are dangerously deadly'.

Let me first congratulate for the fact that you keep so much "knowledge" about pointers. Let me further tell you that all your information about pointers is very very wrong. My contention is that pointers are the simplest thing in the entire C arsenal and the best thing about them is that once you know them, you know them. There is no mugging involved, just plain simple understanding. Pointers are like Buddhism, spend time with them, study them and one day Poof … you'll see enlightenment and pointers will become the easiest thing in the world.

Look at this code:

Program 36

 

main()
{
char i;
*i =10;
}

This will give you a compilation error. Compiler will tell you: Invalid indirection. Reason? Well, you declared the variable as an int (not a pointer) and suddenly you start treating it as a pointer. If i has not been declared as a pointer, you cannot put a * in front of it. C will have none of your inconsistency and will not allow you to proceed further. Make this small change to make C happy:

Program 37

main()
{
char *i;
*i =10;
}

Now, since we have declared i as a pointer, C doesn't mind you treating it as one.

This program should help you clear quite a few cobwebs in your mind about pointers:

Program 38

main()
{
char *s;
s=1047;
*s =64;
}

 

Whenever, you see a * in front of a variable tell yourself two things:

  1. The variable represents 4 memory locations.
  2. Whatever is stored in this variable will represent a computer memory location.

 

Here, s is a pointer. So both rules apply. Next we say:

s=1047;

According to rule (2) 1047 is a computer memory location.

Then we say:

*s=64;

Now, read carefully..! This statement is equivalent to saying:

 1047=64

 Relax, read this as: In computer memory location 1047, we have stored the value 64.

 While on the topic of memory locations, let us tell you an interesting feature.

 Every computer memory location can be thought of as broken into 8 blocks. IN C we begin counting from 0, because the number remains the same, irrespective of where the count starts. Each of these blocks is weighted according to the following scheme:

Hence in the above program when we store 64 in memory location 1047, we are simply putting a '1' in the sixth block. The interesting thing here is that memory location 1047 is special in the sense that each block within this memory location has its own special significance as shown:

 

So, now you realize when you press the Caps-Lock key, what actually happens is that the sixth block in memory location 1047 is set to 1. When the Caps-Lock is removed, this value is made 0. It should be obvious that if you store the value 12 in memory location 1047, and then press the Del key, your computer reboots!!! Why?? Because when you store 12 in location 1047 you are setting the Cntrl and Alt key blocks to 1, and the Del is the last straw, because now you have given your machine the 3 finger salute. Result? Relax till your computer reboots.!!

Program 39

main()
{
int i;
FILE *fp;
}

This will give you an error. The error will say:

Undefined Symbol 'fp'
Undefined Symbol 'FILE'
 

If you wonder why we begin a new concept with an error, it is intentional. Our aim is to drill the right way of writing the code right into your head!

The problem with this program is that:

 

FILE *fp;

is not understood by the compiler. You will have to make this small change and everything works out just fine:

Program 40

#include <stdio.h>
main()
{
int i;
FILE *fp;
}

Lets try to understand the physce of the C compiler. The C compiler is a smart, intelligent and efficient program. Now, smart intelligent and efficient people tend to throw their weight around. The C compiler refuses to scrutinize the code unless someone else has already checked the code. This 'someone else' is the preprocessor. The preprocessor has only one goal in life and that is to go through the code and look for any # (hash) signs. In our program it finds:

#include <stdio.h>

 include is a subdirectory.(c:\borlandc\include). This sub directory contains the file stdio.h and many such files. The file stdio.h contains the statement FILE. If this file(stdio.h) contained 1000 lines of code, our program would appear to the compiler as being 1005 lines long, since the #include <stdio.h> statement is replaced by the entire file.

 Let us now play with files or to be more technically correct, lets look at file handling techniques in C. Type this program but don't run it just yet.

Program 41

#include <stdio.h>
main()
{
int i;
FILE *fp;
fp=fopen("z.c","r");
i=fgetc(fp);
printf("%d..%c\n",i,i);
i=fgetc(fp);
printf("%d..%c\n",i,i);
i=fgetc(fp);
printf("%d..%c\n",i,i);
i=fgetc(fp);
printf("%d \n",i);
}
 

Before you run this program, you need to create a file called z.c in the current subdirectory. For this say: Alt-F Open. Type z.c. You should find yourself staring at a blank screen. This is your file z.c. Save the file (Alt-F Save). Don’t run your program just yet. Read on. We have already looked at the statement FILE *fp. Now let us go further. The next statement is:

fp=fopen("z.c","r");

In the ancient past we had told you that any word followed by a () is understood by C as a function name. Thus, fopen is a function name. We are opening the file z.c for reading or to be more correct we are opening file z.c in the read mode (hence the "r"). From now on, fp stands for file z.c. Whenever we need to refer to file z.c we will use fp. The next statement is:

i=fgetc(fp);

 fgetc is another function, which is used to get values from z.c Since this is the first time the function fgetc is encounterd the first character from z.c will be picked.

Now you realize that we have nothing in z.c to read. Hence we go back to z.c and write this:

 ABC

 Do not press enter after typing the 3 letters. Thus the first character in the file (A) is now in i. Now the first printf is encountered and this character and its corresponding ASCII value are displayed. Now i goes and fetches the next character from the file z.c ( C doesn't seem to forget which character it last picked). So now i contains B which, like before is displayed along with its ASCII value. The same procedure is followed for the third character. Now we have another fgetc statement. But there is no more data in the file z.c. To mark the end of data in a file, C puts a -1. Thus the last printf will display a -1. Thus we have successfully displayed the contains of the file z.c the output looks like this:

A..65
B..66
C..67
-1
 

If you notice the program we just wrote is not too intelligent. It expects us to know how many characters are present in the file. Also, the picking and displaying of value is being done by separate statements each time. Let us make the same program slightly intelligent:

Program 42

#include <stdio.h>
main()
{
int i;
FILE *fp;
fp=fopen("z.c","r");
while((i=fgetc(fp))!=-1)
printf("%d\n",i);
}

Here the most interesting statement is the while statement:

 while((i=fgetc(fp))!=-1)

i picks the first character from the file z.c It compares the value of this character with -1. Since 65 not equal to -1, the ASCII value of the character is displayed. The value -1 is used to represent the state of no more data present in file. Thus, when the ASCII value of C is displayed, I becomes -1 (to indicate no more data is present) and we exit out of the while loop.

There is another important point to note about this while loop. If we said:

while(i=fgetc(fp)!=-1)

We have removed one pair of parenthesis. Will this affect the program?? The answer is :Yes. Reason? By the rule of operator precedence, != is processed first. Thus:

 

fgetc(fp)!=-1

will be processed first. Since fgetc is A, then B and finally C, the output will look like this:

1
1
1

Want to experiment some more? Make the following changes to the file z.c and see the output each time:

z.c	abc	Output: 97 98 99
z.c	A01	Output: 65 48 49
z.c	A-1	Output: 65 (ASCII value of -) 49

How about transferring the contents of one file to another?? Sounds fun? Lets go:

Program 43

main()
{
int i;
FILE *fpi, *fpo;
fpi=fopen("z.c","r");
fpo=fopen("z1.c","w");
while ((i=fgetc(fpi))!=-1)
fputc(i,fpo);
}

fpi stands for file z.c, fpo stands for file z1.c. z.c is opened in read mode(hence the "r"), z1.c is opened in write mode(hence the "w"). The while loop will read data until such time as there is no more data in the file (z.c), at which point the value of i will become -1. Within the while loop we just have a function fputc which puts this value of I (which is read from fpi, which means effectively read from z.c) into fpo(z1.c). Thus, all we are doing is picking the characters from z.c (1 character at a time) and transferring it to z1.c Thus, at he end of this program both z.c and z1.c have the same contents (ABC)

Now let us be more adventurous and try this program:

Program 44

main()
{
int i;
FILE *fpi, *fpo;
fpi=fopen("z.c","r");
fpo=fopen("z1.c","w");
while ((i=fgetc(fpi))!=-1)
{
if (i>=65 && i<='Z')
i=i+32;
fputc(i,fpo);
}
}

As before, fpi and fpo are computer memory locations. fpi stands for file z.c, fpo stands for file z1.c. z.c is opened in read mode(hence the "r"), z1.c is opened in write mode(hence the "w"). The while loop will read data until such time as there is no more data in the file (z.c), at which point the value of i will become -1. Now, within the while loop we have an if statement that checks to see if i contains a capital letter. Look closely at the if statement:

 

if (i>=65 && i<='Z')

The ASCII value of A is 65. Hence this statement could also be written as:

if (i>='A' && i<='Z')

If we remembered the ASCII value of 'Z' we could have put that value in the above statement. The point we are trying to make here is that whenever the C compiler finds any character in single quotes (' '), it goes to the ASCII table to find its ASCII value. Now supposing z.c contains:

AaBb

i picks the first character from z.c ('A') sees it’s a capital letter, adds 32 to the ASCII value of A. 32 because, the difference between the ASCII value of capital 'A' and small 'a' is 32. Now, I contains the ASCII value of small .Thus, the capital A has been converted into small a. This new value of i is written to file z1.c by the statement:

 

fputc(i,fpo);

During the next iteration of the while loop, i picks up the 'a' from z.c. Now the condition in the if statement is false hence i (which is small a) is directly written to file z1.c.

Capital B follows the same pattern as that of capital A, and small b meets the same fate as small a. Hence the file z1.c now contains:

aabb

 Thus we have successfully converted all caps in a file to small.

 Now we are going to write the same program, this time with a bug. Try to find out the problem:

Program 45

main()
{
int i;
FILE *fpi, *fpo;
fpi=fopen("z.c","r");
fpo=fopen("z1.c","w");
while ((i=fgetc(fpi))!=-1)
{
if (i>=65 && i<='Z')
{
i=i+32;
fputc(i,fpo);
}
}
}

When you write this program, z1.c will contain only:

ab

Reason? If you look closely, we have included both statements:

 

i=i+32;
fputc(i,fpo);

within the if statement.(due to the braces). Now when i reads small a, the if statement is false, hence we come out of the if statement. There is no

fputc(i,fpo);

outside the if, hence this value of i ('a') does appear in z1.c. z1.c only contains ab, which are actually the capital A and B of file z.c

Thus, never be sure of the proper working of any program, unless you have run it yourself and seen the output. Run each program step by step and you will never go wrong.

If you have followed the previous program, the next program is very similar:

Program 46

#include <stdio.h>
main()
{
int i;
FILE *fpi, *fpo;
fpi=fopen("z.c","r");
fpo=fopen("z1.c","w");
while ((i=fgetc(fpi))!=-1)
{
if (i>=97 && i<='z')
i=i-32;
fputc(i,fpo);
}
}

Here we are converting the small letters to capital. The only change is the if statement which is now:

if (i>=97 && i<='z')

97 is the ASCII value of small a. here we have:

 

i=i-32;

because of the difference in ASCII values of small and capital letters.

You can use the same files z.c and z1.c. z1.c will now contain:

AABB

Try this interesting program:

Program 47

main()
{
int i;
FILE *fpi, *fpo;
fpi=fopen("z.c","r");
fpo=fopen("z1.c","w");
while ((i=fgetc(fpi))!=-1)
{
if (i>=97 && i<='z')
i=i-32;
if(i>=65 && i<='Z')
i=i+32;
fputc(i,fpo);
}
}

This program is interesting because this program also converts capital letter to small letters. Let us explain how:

i picks A from z.c. The first if statement is false, since it checks only for small letters. Then the second if statement is encountered which is true. Capital A is converted to small a and written to z1.c. Now when i picks small a from z.c, the first if statement is true, small a is converted to capital A. Now the second if statement is encountered, which again is true.(because now i contains capital A). This if statement converts capital A back to small a which is written to z1.c. the net result is that z1.c contains only small letters. Interesting, eh??

Note, 2 programs which look so different do the same thing. (programs 43 and 45). Although, this program is not terribly efficient, the point is, and we repeat, run each program and see the output.

Next, we will make a small change to program 45 and find that capital letters become small and vice-versa.

Program 48

#include <stdio.h>
main()
{
int i;
FILE *fpi, *fpo;
fpi=fopen("z.c","r");
fpo=fopen("z1.c","w");
while ((i=fgetc(fpi))!=-1)
{
if (i>=97 && i<='z')
i=i-32;
else if(i>=65 && i<='Z')
i=i+32;
fputc(i,fpo);
}
}

The only change in this program is the inclusion of the word else. Now when i is a small letter the first statement is true, which converts i to capital. If i is capital to start with, the first if statement is false, the second one is true, and I now contains a small letter. So starting with file z.c which is:

AaBb

z1.c looks like this:

aAbB

Our next endeavor will be to remove all spaces from a file. For this first make the following change to z.c:

A B C

Now run this program:

Program 49

#include <stdio.h>
main()
{
int i;
FILE *fpi, *fpo;
fpi=fopen("z.c","r");
fpo=fopen("z1.c","w");
while ((i=fgetc(fpi))!=-1)
{
if (i!=32)
fputc(i,fpo);
}
}

Enough has been said about the first few statements of this program. Lets look at the if statement:

 

if(i!=32)
fputc(i,fpo);

32 is the ASCII value of space. (We could have used ' ' instead of 32). So, in English the if statement would mean: only if i does not contain a space, put it into fpo.(z1.c). Thus, all spaces in the file z.c vanish into thin air. z1.c now looks like this:

ABC

Another way to write the same program would be:

Program 50

#include <stdio.h>
main()
{
int i;
FILE *fpi, *fpo;
fpi=fopen("z.c","r");
fpo=fopen("z1.c","w");
while ((i=fgetc(fpi))!=-1)
{
if (!(i==32))
fputc(i,fpo);
}
}

Here, the if statement is slightly different. It says:

 if(!(i==32))
fputc(i,fpo);

Let us remind you, that whenever you see a double equal to (==), it means you are asking a question. So here, if i indeed contains a space( ASCII 32), (I==32) will be true, or 1, hence the if statement now becomes:

if(!(1))
fputc(I,fpo);

!1 is 0. Hence the statement now becomes:

 

if(0)
fputc(i,fpo);

 

This will cause the statement:

fputc(i,fpo);

to be ignored. Hence the spaces in z.c will not figure in z1.c

Yet another way of accomplishing the same task is:

Program 51

#include <stdio.h>
main()
{
int i;
FILE *fpi, *fpo;
fpi=fopen("z.c","r");
fpo=fopen("z1.c","w");
while ((i=fgetc(fpi))!=-1)
{
if (i==32)
;
else fputc(i,fpo);
}
}

Here the if statement says:

if (i==32);

which in simple English means: if I contains a space do nothing. That’s right nothing. If that is not the case(i contains anything else except a space), we jump to the else statement which is:

 else fputc(I,fpo);

 meaning, write it to z1.c. Again, spaces won't find any space in z1.c

Three different programs, three same outputs. Is that a coincidence?? No sir! No way!! We have taken the pain to demonstrate just one thing: Different people have different styles of coding. It is imperative that you understand the code in plain and simple English. Never, we repeat never, try to remember a program. Write your own code. Syntax is just one aspect of learning a programming language. The real challenge is in understanding and developing the logic for the program. And logic unfortunately cannot be taught, it can only be discovered. Its like a cricket batsmen's timing of shots. Let him read a trillion books on batting, only if he practices hard and long will he find his touch. Ditto for logic.

We suddenly feel the urge to remove all numbers from our file. So here we go. Before we write the program, make the following changes to z.c:

A19B

Here is the program:

Program 52

#include <stdio.h>
main()
{
int i;
FILE *fpi, *fpo;
fpi=fopen("z.c","r");
fpo=fopen("z1.c","w");
while ((i=fgetc(fpi))!=-1)
{
if ((i>='A' && i<='Z') || (i>='a' && i<='z'))
fputc(i,fpo);
}
}

Run this program. The output in file z1.c will look like this:

AB

Happy aren't you. But there is a small problem. The if statement within this program only checks if z.c contains capital or small letters:

 if ((i>='A' && i<='Z') || (i>='a' && i<='z'))

What if my z.c contains:

 

A19,B

 Now the aim of our program is to remove all numbers from z.c. the comma(,) in z.c is not a number. So, it should appear in the output file z1.c. here our program fails. But, never give up till the fight is over. We will modify out code to solve our problem. Here goes:

 Program 53

#include <stdio.h>
main()
{
int i;
FILE *fpi, *fpo;
fpi=fopen("z.c","r");
fpo=fopen("z1.c","w");
while ((i=fgetc(fpi))!=-1)
{
if (!(i>='0' && i<='9'))
fputc(i,fpo);
}
}

Aha! Run this program and z1.c looks like this:

A,B

 The change here is in the if statement:

 if (!(i>='0' && i<='9'))

 Here we search for numbers(0 -9) in the file z.c. Upon finding one, we invert the condition hence we have:

if (!(1))
fputc(i,fpo);
 

As, explained above, this will ignore:

fputc(i,fpo);

And hence no numbers figure in z1.c

 Remember, ! (not) inverts the condition, True becomes False, False becomes True.

 Why not remove all numbers from a file and replace them with spaces. Are you game? Lets go:

 Program 54

#include <stdio.h>
main()
{
int i;
FILE *fpi, *fpo;
fpi=fopen("z.c","r");
fpo=fopen("z1.c","w");
while ((i=fgetc(fpi))!=-1)
{
if (i>='0' && i<='9')
i=32;
fputc(i,fpo);
}
}

 here again the trick is in the if statement:

if (i>='0' && i<='9')
i=32;

If i contains a number, it is changed to a space and then written to the output file, otherwise the value of i is directly written to the output file.

Alright, next task. We want to convert space in out input file z.c into five spaces in the output file z1.c

Go ahead, get your fingers moving:

 Program 55

#include <stdio.h>
main()
{
int i;
int j;
FILE *fpi, *fpo;
fpi=fopen("z.c","r");
fpo=fopen("z1.c","w");
while ((i=fgetc(fpi))!=-1)
{
if (i==32)
{
for (j=0;j<=4;j++)
fputc(32,fpo);
}
else
fputc(i,fpo);
}
}

The if statement here is interesting: 

if (i==32)
{
for (j=0;j<=4;j++)
fputc(32,fpo);
}

Thus, when a space is encountered, the if statement becomes true, and we enter the for loop. Here we step through the for loop 5 times, each time putting a space in the output file. Thus when we come out of the if statement, bingo we have converted the single space into 5 spaces. Of course, i contains any other character(except a space), that character is directly written to the output file.

Lets tamper with our code and check. Just replace the for loop to look like this:

for(j=1;j<=5;j++)

 Same output.

 Lets not stop here:

 Program 56

#include <stdio.h>
main()
{
int i;
int j;
FILE *fpi, *fpo;
fpi=fopen("z.c","r");
fpo=fopen("z1.c","w");
while ((i=fgetc(fpi))!=-1)
{
if (i==32)
for (j=0;j<=4;j++)
fputc(32,fpo);
fputc(i,fpo);
}
}
 

We have just removed the word else. Now, run this program and hey, we have 6 spaces replacing the single space. But how did this happen? My friend, you have committed this heinous crime with your own little fingers. The for loop is executed 5 times, as before, each time writing to the output file. Now when the C compiler exits out of the for loop, the next statement it encounters is :

 fputc(i,fpo);

Aha! This statement is the culprit. This statement causes the sixth statement to appear in the output! (Smart work Sherlock!!). This should cause your creative juices to flow and you should now look for a way to print 5 spaces even when this statement is present. How do you do it? Type this:

Program 57

#include <stdio.h>
main()
{
int i;
int j;
FILE *fpi, *fpo;
fpi=fopen("z.c","r");
fpo=fopen("z1.c","w");
while ((i=fgetc(fpi))!=-1)
{
if (i==32)
for (j=0;j<=3;j++)
fputc(32,fpo);
fputc(i,fpo);
}
}

Good, now the for loop is executed 4 times, causing 4 spaces to be written to the output file. The fifth space will be taken care of by the culprit statement (thereby absolving it from it's earlier crime).

Now Mr. Detective make the following change to file z.c:

A B

and deduce the output of this program:

Program 58

#include <stdio.h>
main()
{
int i;
FILE *fpi;
FILE *fpo;
fpi=fopen("z.c","r");
fpo=fopen("z1.c","w");
while((i=fgetc(fpi))!=-1)
{
if (i==32)
{
while ((i=fgetc(fpi))==32)
;
}
fputc(i,fpo);
}
printf("%d\n",i);
}

Lets see how this one works: i picks up A from z.c. This is not a space hence the if statement is false and A is written directly to z1.c. Next i picks a space from z.c. It enters the if statement. Here the statement:

while ((i=fgetc(fpi))==32)

causes the next character in file z.c to be picked, which also happens to be a space. When a semicolon is encountered, you are telling the C compiler to do nothing. Then the close brace('}') is encountered. This causes the compiler to go to the if statement again. The if statement is true (this time due to the second space). Again, the while loop causes the third space to be picked up, again discovers that no action is to be taken, comes back to the if statement again. Once again the if statement is true(since now contains the third space). This time the while statement picks up the last character from z.c(B). No action hence the compiler goes back to the if statement. Now, since i contains B(not a space), the if condition is false, the program jumps to the statement:

fputc(i,fpo);

 and writes B to the output file.

 So, like before we have removed spaces from our file. Phew!!!

 We will now write a program which will scrutinize your file and tell you if your file is an ASCII file or not. An ASCII file is one which contains any characters whose ASCII values are in the range 1 to 128.

 Here goes:

 Program 59

#include <stdio.h>
main()
{
int i;
int j;
FILE *fpi;
j=0;
fpi=fopen("z.c","r");
while((i=fgetc(fpi))!=-1)
{
if (i>=128)
j++;
}
if (j!=0)
printf("not an ASCII file");
else
printf(" an ASCII file");
}

Here the first if statement is:

if (i>=128)
j++;

Here in the if loop we check to see if any of the characters in file z.c has an ASCII value between 1 and 128. If it does we increment the value of j by one.(j is initialized to 0).

 

if (j==0)
printf("not an ASCII file");
else
printf(" an ASCII file");

 

Now, at the end of this if loop j contains value 0, it only means that z.c does not contain any ASCII characters, hence we display "not an ASCII file". If j is not equal to 0, the file is ASCII.

File Compression and Decompression

We as a human race are a species of impatient creatures. No wonder then that "instant" food is such a big success these days. Computers are no different.(after all we humans invented this mean machine). There is such a lot of talk about the revolution of the Internet, the information superhighway and all that. But do you realize that all these "super" technologies still rely on our "super" slow telephone systems which are meant for voice transmission not data transmission. Hence there is a constant need to convert our files into smaller sized files, commonly termed as compressing or zipping files. Presented below are a few basic techniques of file compression and decompression, which can be considered the basis of all these current complex compression/decompression techniques.

 We have written these programs on the basis of the assumption that a space is the most commonly occurring character in a file. Also, our program will work only in case of ASCII files. Let us understand the compression program: Make your z.c look like this:

 A BAD CAT

 Now, run the following program:

 Program 60

#include <stdio.h>
main()
{
int i;
int j;
FILE *fpi;
FILE *fpo;
fpi=fopen("z.c","r");
fpo=fopen("z1.c","w");
while((i=fgetc(fpi))!=-1)
{
if (i==32)
{
i=fgetc(fpi);
i=i+128;
}
fputc(i,fpo);
}
}
 

The first character picked from z.c is A. This does not satisfy the condition in the if statement which is:

 

if (i==32)
{
i=fgetc(fpi);
i=i+128;
}

hence A gets written to z1.c

Now the next character is a space. Now the condition in the if statement is true. Within the if statement we pick the next character from z.c (in our case B) and add 128 to the ASCII value of this character (66+128). This results in the display of the character corresponding to ASCII value 194. Thus we have reduced two characters (space and B into one character in the output file z1.c. Continuing in the same way, the A and D of BAD get picked from file z.c and are written to z1.c directly. Again when the space is encountered the next character is picked from file z.c (in our case) and 128 is added to the ASCII value of this character (67+128). The output file now has the character corresponding to 195. The A and T are written directly to z1.c.

Hence we you notice that our input file z.c was 9 bytes long. Z1.c is only 7 bytes long, a sure sign of file compression. (To find the size of the files go to the directory in which the files are present, do a dir and you will see the size of the file corresponding to the name of the file.)

Now we will look at a program for decompressing or inflating our file. For this we will take our compressed file z1.c and put the uncompressed file into z2.c. Creating files has been explained already.

Program 61

#include <stdio.h>
main()
{
int i;
int j;
FILE *fpi;
FILE *fpo;
fpi=fopen("z1.c","r");
fpo=fopen("z2.c","w");
while((i=fgetc(fpi))!=-1)
{
if (i>=128)
{
i=i-128;
fputc(32, fpo);
}
fputc(i,fpo);
}

We will now write a program which works much better when the input file contains a string of identical successive characters. In our example we have assumed the input file contains successive spaces. For this make your file z.c look like this:

A BAD CAT 

Program 62

#include <stdio.h>
main()
{
int i;
int j;
FILE *fpi;
FILE *fpo;
fpi=fopen("z.c","r");
fpo=fopen("z1.c","w");
while((i=fgetc(fpi))!=-1)
{
if (i==32)
{
j=1;
while((i=fgetc(fpi))==32)
j++;
fputc(j+128, fpo);
}
fputc(i,fpo);
}
}

Here the if statement is more intelligent:

if (i==32)
{
j=1;
while((i=fgetc(fpi))==32)
j++;
fputc(j+128, fpo);
}

Inside the if statement we count the number of successive occurrences of spaces. This value is stored in variable j. We enter the if statement only when we encounter a space. Hence j is initialized to 1. Then the next character is picked. If this happens to be a space, j is incremented. All consecutive occurrences of space are recorded in j. Thus if we have 5 spaces, j would be 5. Then we have the statement:

fputc(j+128, fpo);

Here the ASCII value corresponding to 5+128 i.e. 133 is written to the output file z1.c. Thus the 5 spaces have now been replaced by a single character. The 3 spaces between BAD and CAT are similarly replaced by a single space.

Thus, the input file has been compressed from 15 bytes to only 9 bytes.

Now that we have compressed our file, lets write a program for decompressing or inflating our file. For this we will take our compressed file z1.c and put the uncompressed file into z2.c. Creating files has been explained already.

Program 63

#include <stdio.h>
main()
{
int i;
int j;
int k;
FILE *fpi;
FILE *fpo;
fpi=fopen("z1.c","r");
fpo=fopen("z2.c","w");
while((i=fgetc(fpi))!=-1)
{
if (i>=128)
{
k=i-128;
for(j=1;j<=k;j++)
fputc(32,fpo);
}
else
fputc(i,fpo);
}
}

Here when C encounters any character in file z1.c with an ASCII value greater than or equal to 128, it knows that compression has taken place at that particular location. Hence we have the statement:

k=i-128;

 

This statement is used to record the number of spaces at this location. Now we use this value of k as the limiting value in the for loop:

for(j=1;j<=k;j++)
fputc(32,fpo);

Thus, from our previous example k would be 5, the for loop would go on 5 times and 5 spaces would be written to file z2.c. Any character other than the space will not enter the if statement and will be written directly to z2.c by the else statement.

Arrays

 We will now talk about a very handy concept in C. Look at the following code:

 Program 64

main()
{
int i;
char a[5];
a[0]=1;
a[1]=2;
a[2]=3;
a[3]=4;
a[4]=10;
i=0;
printf("%d\n",a[i]);
i=1;
printf("%d\n",a[i]);
i=4;
printf("%d\n",a[i]);
}

int i, takes 2 memory locations and names them 1. i is thus a variable. Then we see:

char a[5];

Aha! What have we done here? We have, in one stroke created 5 variables of type char. C starts counting from 0, hence these variables are: a[0], a[1], a[2], a[3], a[4]. Then we have 3 printf's that look exactly the same. The important thing to note here is that a[i] is not a variable. a[0] …..a[4] are variables. When i=0, a[i] = a[0]. Thus when we write a[I] we are not writing the name of a valid variable. Some other variable (i) decides the name of the variable, hence the value. Thus, we have added a new level of abstraction into our code. It is concepts such as these that come in very handy when writing complex examples.

a[i] is an array. Arrays are one of the most powerful tools of any programming language. The good thing about arrays is that they can be put into loops, like any other variables. This as you realize will greatly simply tasks which would otherwise be long and tedious. Let us demonstrate this point with two programs:

Program 65

main()
{
int a[5];
int i;
for (i=0;i<=4;i++)
a[i]=i*2;
for (i=0;i<=4;i++)
printf("%d\n",a[i]);
}

Here we have defined a array of type int of size 5. Then we put this array into a loop. Within this loop we conviniently put this array and manipulate the array at the same time. Thus the output is:

 
0
2
4
6
8

How many letters in the English alphabet?? Twenty six!! What if we were to write a program that counted the number of occurrences of each alphabet in our file!! Possible? Yes, but wouldn't we need 26 variables for storing the occurrence of each alphabet. Along with these 26 variables, we would have 26 if statements. Since we are using 26 variables, each of these would have to be initialized. Sounds a long and boring program to write, right? Look at the following program to realize how helpful arrays can be in simplifying matters. Before writing the program make sure your file z.c looks like this:

AABBBCCDDEFFFF

 Program 66

#include <stdio.h>
main()
{
int i;
long a[26];
FILE *fp;
for(i=0;i<=25;i++)
a[i]=0;
fp=fopen("z.c","r");
while((i=fgetc(fp))!=-1)
{
if (i>='A' && i<='Z')
a[i-65]++;
}
for(i=0;i<=25;i++)
printf(" %c..%d",i+65,a[i]);
}
 

Here we create an array a of size 26 (equal to number of alphabets) and of type long. Remember, long reserves 4 memory locations and can thus store 4 billion occurrences of each letter. The first for loop is just to initialize all the 26 variables, a[0] ….a[25] to zero. Then the if statement checks for capital letters in the file. (We are limiting our program to capital letters only, small letters can also be counted with just another if statement). Now pay close attention: The first letter picked up is A. The ASCII value of A is 65. Hence the statement:

 a[i-65]++;

We will break this statement. First consider:

a[i-65]

This now becomes a[65-65] i.e. a[0]. Thus the occurrence of A is recorded in a[0]. Following the same logic when a B (ASCII value 66) is encountered, we have: a[66-65] i.e. a[1]. Thus the occurrence of B are recorded in a[1]. Following the same logic, a[3] will contain the occurrence of C … a[25] will contain the occurrence of Z. Now, if any of the alphabets is encountered again, we are able to keep track of it by this statement:

a[i-65]++;

Thus the second time A is encountered a[0] is incremented to 2. Thus, the 26 variables, a[0] to a[25] are used to store the occurrences of the 26 letters A through Z. Then the for loop is used to display the output:

for(i=0;i<=25;i++)
printf(" %c..%d",i+65,a[i]);

i+65 will actually be 0+65, 1+65 … as i gets incremented. Since we have a ‘%c’ corresponding to I+65, the character equivalent of the ASCII values will be printed. A[i] will be a[0] a[1] .. a[25] as i gets incremented. Thus corresponding to ASCII value 65 (A) we have a[0] which contains nothing but the number of occurrences of A in the file. Hence the complete output looks like this:

A..2 B..3 C..2 D..2 E..1 F..4 G..0 H..0 I..0 J..0 K..0 L..0 M..0
N..0 O..0 P..0 Q..0 R..0 S..0 T..0 U..0 V..0 W..0 X..0 Y..0 Z..0

As kids, we were introduced to the decimal form of numbers. The decimal form of numbers is so called because it contains 10 symbols:

0 1 2 3 4 5 6 7 8 9

 Since we run out of symbols, we combine the first two symbols ( 0 and 1) to create the next number: 10.

Computers prefer the hexadecimal form of numbers. Here there are 16 symbols:

0 1 2 3 4 5 6 7 8 9 A B C D E F

As before, the next number is created by combining the first two symbols: 10

Illustrated below is a program which plays around with numbers of these types:

Program 67

main()
{
int i;
i=20;
printf("%x\n",i);
i=0x15;
printf("%d\n",i);
i=0x14;
printf("%d..%x\n",i,i);
i=0x10+10;
printf("%d..%x\n",i,i);
}

In the first printf we will display the hexadecimal equivalent of 20. (%x displays the hexadecimal equivalent of a number). Then we have:

i=0x15;

Whenever the C compiler comes across any number with a 0x in front of it, it understands the number as a hexadecimal number. Thus I now contains the hexadecimal number 15. The second printf displays the decimal equivalent of hex 15 (because of %d). Then we have:

 i=0x14

Now the value of I is hex 14. The third printf displays the decimal equivalent of hex 14 as well as the hex value itself (because of the %d and %x).

The next statement is interesting:

i=0x10+10;

Here we want to add the hex number 10 with the decimal number 10. When C comes across such a statement it gets pretty upset. It yells at you: ' Hey why can't you make your mind on which type of numbering system you want to use'. But then C is like a traditional Indian woman who does her duty in spite of her discomfort. So the last printf faithfully reflects the numbers in both decimal and hex forms.

Output

14
21
20..14
26..1a

In this world full of pretence, the way you look can often decide how far you go in life. Unfortunate, but true!! This trend is not just limited to the social circles, it has affected the software industry too. A software with a good looking interface can often tilt the scale in its favor. Realizing the importance of being able to talk to the display device directly, Kernighan Richie, decided to give programmers the ability to directly talk to the screen. In this section we will explore this aspect of C.

Look at this piece of code:

main()
{
char *s;
}

Lets be honest with ourselves. Whenever, we see a * in front of a variable, there is a sense of uneasiness. Its like examination time. No matter how well you are prepared, the moment the question paper comes to you, you feel extremely uneasy, almost fidgety. Here we see s with a *. s is a variable and like all other variables it has to be initialized. So we add this line to our code above:

s=0xb8000000;

We have already told you that a number preceded by a '0x' is understood by C as a hexadecimal number. Thus, what we mean by the above statement is that s now contains the address of memory location b8000000. The reason we have used hexadecimal is that it is a more convenient way of representing a number. Representing this hex number with a decimal we would have been a nightmarish task. As far as the computer is concerned, it really doesn't care about the way you represent numbers(decimal or hex).

Now, we will add another statement to our code:

*s=65;

This means that we are putting the value 65 in memory location b8000000. Our final program looks like this:

Program 68

main()
{
char *s;
s=0xb8000000;
*s=65;
}

Now run this program and see the output. You will see an 'A' on the top left corner of your screen. If you were a detective (with a computer background) you would smell foul play. You know that 65 is the ASCII value of A. But what about memory location 0b8000000. Hmmm, something tells you this guy is the bad guy. You decide to make him your top suspect and probe further.

You decide to delve deep into the case. For this you decide to understand the following programs so that you have enough evidence to nail your suspect. You decide to run the following programs:

Program 69

main()
{
int i,j;
for(i=1;i<=50;i++)
{
for(j=0;j<=3998; j=j+2)
printf("A");
}
}

In the course of your investigation you find out that the screen has 80 characters/row and 25 rows

:

Hence, the second for loop starts from 0 and is executed 2000 times, each time being incremented by two. The output of this program is: 1 screen with 2000 A's and 50 such screens.

Now you follow your second lead. You run this program:

Program 70

main()
{
int i,j;
char *s;
s=0xb8000000;
for(i=1;i<=50;i++)
{
for(j=0;j<=3998; j=j+2)
*(s+j)=65;
}
}

Hmmm, now you seem to be getting somewhere. The *s and memory location b8000000 have figured again. This program gives you the same output as the previous program, the only difference being that this program is perceptibly faster than the previous program. Now what does this mean?? It means that by using *s we are able to communicate directly with the memory location b8000000 and this is the reason this program runs faster. Thus, you now understand the power and utility of the special variables with a * in front of them.

You are now close to solving the mystery.

The human eye has a limitation. If the screen is not refreshed fast enough, we tend to see a blurred image. Thus, it is imperative to refresh the screen constantly at a certain minimum rate. Now if our 'main' computer which does all the processing, input output had to do the screen refreshing bit too, it would obviously become slow. Because the screen has to be refreshed or redrawn very often. Thus, if we could have another small computer within my main computer whose only function would be to redraw the screen at the required rate, my main computer would be less burdened and thus work faster. This small computer would have to be given a area of memory and be told to constantly refresh only this area of memory. Then we would store our screen in this same memory area, and thus the small computer would constantly keep refreshing the screen at the required rate. That would then stop the blurring effect. Now that you are ready with the arrest warrant, you begin to track the suspect.

Try this program:

Program 71

main()
{
char *s;
s=0xb8000000;
*s=65;
*(s+2)=67;
*(s+4)=48;
}

The output of this program is:

AC0

Now it is easy for you to understand that somewhere in memory the following scenario exists:

Now the small computer goes to the first of this memory location (b8000000) and sees a 65 (A). Then it goes to memory location b8000002 and sees a 67 (C) and finally encounters 48 in memory location b8000004. We have ignored the odd memory location here. We will explain them soon.Time to understand the structure of the computer memory.

It has 2 basic parts: ROM and the RAM. ROM: stands for Read Only Memory. Contents of this part of memory are not lost if the power is switched off. The part of memory can only be read from, not written to.

RAM: stands for Random Access Memory. Contents of this part of memory are lost once the power is switched off. This part of memory can be read from and written to.

RAM is random access, which means that if we need to go to the tenth record in RAM we do not need to start reading from the first record. We can directly jump to record number 10. ROM is also random access. In that sense, ROM is also RAM!! Thus the name ROM is misleading and hence it would be more correct to call ROM, RWM which stands for Read Write Memory.  

Operating systems have a peculiarity: they do not like to communicate with the hardware. This is because an OS recognizes one type of hardware. If you install new hardware, the OS will have to recognize this new hardware. This amounts to work, and like everyone in this universe, the OS shuns work. They need some other programs to talk to hardware. ROM is a collection of small programs, which can talk to the hardware. It is for this reason that ROM is also called ROM BIOS, where BIOS stands for Basic Input Output System. Thus whenever you say:

printf("hi");

printf doesn't know how to print hi, it expresses its inability to DOS. DOS says: 'But I cant talk to hardware. I can't print hi. Then DOS negotiates a deal with one of the small programs in the ROM BIOS to print hi. This program then goes to memory location b8000000 and prints hi. This, quite obviously is an extremely slow and inefficient method of doing things. By directly talking to memory location b8000000 we are bypassing all these intermediate steps and thereby increasing program execution speeds.

Now that we are in talking terms with the screen memory lets be naughty:

main()
{
char *s;
int i;
s=0xb8000000;
for(i=1;i<=3998;i=i+2);
*(s+i)=32;
}

Here, when i=0, *(s+i) becomes (*S) which is effectively * b8000000 and in this location we put a space( ASCII 32 is a space). Then, when i=2, (s+i) becomes *(s+2) which is effectively * b8000002 and there again we put a space. This process is repeated 2000 times and the end result is that we clear the screen. (all spaces).

Continuing in the same spirit, run this program:

Program 72

main()
{
char *s;
int i;
s=0xb8000000;
for(i=1;i<=3998;i=i+2);
{
if(*(s+i)>='A' && *(s+i)<='Z')
*(s+i)=32;
}
}

Here, within the for loop (which again goes on 2000 times) we have an if statement which checks for capital letters on the screen. When a capital letter is encountered, it is replaced by a space.

Alright, one last program:

Program 73

main()
{
char *s;
int i;
s=0xb8000000;
for(i=1;i<=3998;i=i+2);
*(s+i)=*(s+i)+32;
}

Here, whenever we encounter a capital letter on screen, we are converting it into a small letter.

Till now, we have only talked about even memory locations. Let us now turn our eyes to the odd memory locations within the screen memory.

If you remember we had told you about a certain memory location 1047, where the status of the keyboard (shift, alt,….) was stored. In memory location b8000000, we have a similar concept. Look at the following diagram:

The first 3 bits (0,1,2) are for setting the foreground color. Thus we can have 8 different foreground colors. The fourth bit (bit 3) is the intensity bit. The next three bits (bits 4,5,6) are for setting the background color. Thus we have a choice of 16 background colors. The last bit (bit 7) is for setting the blinking feature ON. Now let us use this new found knowledge to add color to our life:

Program 74

main()
{
int i;
char *s=0xb8000000;
for(i=1;i<=3999;i++)
*(s+i) =2;
}

A few things to notice in this program. One: it doesn't matter if you declare the int I first or you declare char *s first. Not an issue. Second: You can initialize the variable at the time of declaration, C doesn't mind. And finally the output: by saying 2 you have set the foreground color. We don' remember what color 2 stands for. But we don’t mind you changing this value (from 0 to 7) and checking all colors. We personally feel there are more important things in life than being choosy about colors. Now run this program:

Program 75

main()
{
int i;
char *s=0xb8000000;
for(i=1;i<=3999;i++)
*(s+i) =2+8;
}

By saying:

*(s+i) =2+8;

you have kept the foreground color 2, but have set the intensity bit. What happens by this is best explained by seeing the change on your monitors. The intensity bit is 8, because all these bits have been given weights.

 A little more experimentation:

 Program 76

main()
{
int i;
char *s=0xb8000000;
for(i=1;i<=3999;i++)
*(s+i) =2+8+48;
}
 

Now we have foreground color2, intensity bit on and background color 48 (i.e. we are making bits 4 and 5 one) Look at the color, if it doesn't go with your trousers or skirts, feel free to make any change. You can try setting the foreground color by changing 48 to 64 or 16 .. go ahead experiment.

Another variation:

Program 77

main()
{
int i;
char *s=0xb8000000;
for(i=1;i<=3999;i++)
*(s+i) =2+8+48+128;
}

here the foreground color is 2, intensity high, background color 48, and the blinking bit set. Statutory Warning: Staring at a blinking screen (with all sorts of weird colors) is injurious to health.

And finally, the icing on the cake:

Program 78

main()
{
char *s;
int i,j;
s=0xb8000000;
for(i=1;i<=3998;i=i+2);
{
if(*(s+i)>='A' && *(s+i)<='Z')
*(s+i+1)=2;
if(*(s+i)>='0' && *(s+i)<='9')
*(s+i+1)=3;
if(*(s+i)>='a' && *(s+i)<='z')
*(s+i+1)=4;
}
}

Here all the capital letters will have color corresponding to 2, numbers will have a color corresponding to 3, small letters will have color corresponding to 4.

File handling and screen manipulation --- two great features incorporated into the same program. Lets look at some such programs:

Program 79

#include <stdio.h>
main()
{
char *s;
int i,j;
FILE *fp;
s=0xb8000000;
fp=fopen("z1.c","w");
for(i=0;i<=3999;i++)
{
j=*(s+1);
fputc(j,fp);
}
}

We will talk about the for loop only:

for(i=0;i<=3999;i++)
{
j=*(s+1);
fputc(j,fp);
}

We execute the for loop 4000 times. First we capture the on screen character, put it into variable j, then the characters attribute is captured and put into variable j. At each step we transfer the contents of j into file z1.c which is in write mode. The end result is that we have successfully captured the entire screen and transfers it to file z1.c. open z1.c for proof.

Now we will do the opposite of the previous program. Transfer the contents of file z.c onto the screen:

Program 80

#include <stdio.h>
main()
{
char *s;
int i,j;
FILE *fp;
s=0xb8000000;
fp=fopen("z.c","r");
for(i=0;i<=3999;i++)
{
j=fgetc(fp);
*(s+i)=j;
}
}

Here the for loop is:

for(i=0;i<=3999;i++)
{
j=fgetc(fp);
*(s+i)=j;
}

we pick the characters from file z.c, store it into variable j and then write them onto the screen one at a time. The first character will be displayed at memory location b8000000(top left), followed by its attribute, then the second character, its attribute and so on…! We could have saved a statement by writing: 

for(i=0;i<=3999;i++)
{
*(s+i)=fgetc(fp)
}

But, remember that the Income Tax department is so far kind enough to not tax us on the number of variables we use in our C code. Hence don't shy away from using as many variables as possible. It just adds to the clarity of the logic and helps avoid errors.

One last program on file handling and screen memory:

Program 81

main()
{
char *s;
int i;
char a[4000];
s=0xb8000000;
for(i=0;i<=3999;i++)
a[i]=*(s+i);
for (i=0;i<=100;i++)
printf("%d\n",i);
for(i=0;i<=3999;i++)
*(s+i)=a[i];
}

This program saves the scren and then restores the screen. This program captures the screen into an array, then changes the display and in the end revert back to our old screen. Here we have defined an array of data type character of size 4000.

In the first for loop:

for(i=0;i<=3999;i++)
a[i]=*(s+i);

we are storing each of the character and its attribute into the array a[I]. This for loop goes on 4000 times, hence we have an array of size 4000.

In the next for loop:

for (i=0;i<=100;i++)
printf("%d\n",i);

we change the display and display the first 100 numbers on screen. Here we are struck by nostalgia and we feel like looking at our dear old screen. The third for loop gets the old screen back:

for(i=0;i<=3999;i++)
*(s+i)=a[i];

Functions

C provides us with a bag full of functions. In addition to that we can also create our own functions, and call them as and when we like. The next few programs will demonstrate just that:

Program 82

main()
{
abc();
}
abc()
{
printf("abc");
}

Here within the main we are calling function abc(). abc() is not a function provided by C. It is a user defined function. Hence it is the duty of the user to create the function. For this we have:

abc()
{
printf("abc");
}

Whenever you see a function name with no semi-colon (;) following it, you should know that this is where the function is being created. Here the function abc() just prints abc. Hence the output of this program is:

abc

 To drill the point into your brain, look at the next program:

Program 83

 

main()
{
printf("confusion");
main();
}

Here we are first creating main (absence of semi-colon). Then in the last line we are calling main(). Output: A screenful of confusion The reason is the function main() keeps is called over and over again. Your computer might even give you some funny error messages.

Now, lets play with functions:

Program 84

main()
{
abc();
pqr();
}
abc()
{
printf("abc\n");
}
pqr()
{
printf("pqr\n");
abc();
}

Here, we first call function abc(). Function abc() prints for us abc. Then we call function pqr(). In pqr() we first print pqr and then from within pqr() we call abc(). Thus abc is printed again. Hence the output is:

abc
pqr
abc

Lets get more adventurous. Don’t run this program just yet:

Program 85

main()
{
abc();
pqr();
}
abc()
{
printf("abc\n");
pqr();
}
pqr()
{
printf("pqr\n");
abc();
}

Lets first understand: first we call function abc(). Within abc() we call pqr(). The compiler now jumps to function pqr(). Here it finds function abc() being called again. It jumps to abc(), only to find function pqr() being called. End result: not too pretty. The C compiler is stuck. Your computer might hang, you might have to restart the machine. You might have to press Ctrl-break to get out of the vicious circle.

All our previous examples had functions with no parameters. Let us go one step forward and look at functions with parameters:

Program 86

main()
{
abc(100,200);
}
abc(x,y)
int x,y;
{
printf("%d .. %d\n",x,y);
}

Here we call function abc with 2 parameters:

 

abc(100,200);

Thus we also have to create the function abc with two parameters which we have done in this statement:

abc(x,y)

The C compiler will have to be told about the data type of these two parameters. Hence we say:

int x,y;

Then the printf will display:

 100 .. 200

Another program with functions and parameters:

Program 87

main()
{
int i=20;
int j=40;
abc(100,200);
abc(i,j);
abc(i*50,j*20);
}
abc(x,y)
int x,y;
{
printf("%d .. %d\n",x,y);
}

The way we have created the function is identical to the previous program. The point we are trying to demonstrate is that when functions are called, the parameters to be are fixed first. If this involve some calculation, these calculations are performed first. In our program, the first call to abc() has two parameters 100 and 200 which are explicitly specified. Then abc() is called with parameters i and j. These variables have been initialized to 20 and 40. In the last call to function abc() a calculation is involved to determine the parameters. The calculation is performed first and then the call is completed. Hence the output will be:

100 .. 200
20 .. 40
1000 .. 800

 

The Seemingly Endless Sea Of pointers

You are now in the second week of the course and know quite a few things about C. Everything nice and interesting. Now, you need to change levels. You are now about to enter the twilight zone. Here things are not so rosy, hard work is called for. It is here that the men will be separated from the boys. And from here on the distance will only keep increasing. So, don't loose out. Work hard, more importantly, work smart.

You have been introduced to pointers. But that was just the tip of the iceberg. Pointers is perhaps the most powerful feature of C, and consequently the most dangerous. After all power and danger go hand in hand. We suggest that you go through the pointer part of the notes extremely carefully and meticulously. If you haven't understood a line, don't go ahead. Pointers is not your daily bread and butter.

 

Take a deep breath and try this program:

Program 88

main()
{
int i;
printf("%p\n",&i);
}

One statement at a time:

int i;

 Here we are reserving two memory locations and calling them i. These locations are reserved somewhere in memory. You and me don’t know where. But the curious creatures that we are, we would like to know. C grants you this wish when it sees the next statement:

 printf("%p\n",&i);

 

This printf is different from any of the printf’s we have seen so far in two respects. One: we have a %p and second is the ‘&’ before variable i. The %p is to tell the C compiler that the value that will be displayed by this printf is a computer memory location. ‘&’ (ampersand) can be put only in front of a variable. When C sees the ‘&’ followed by the variable name, it faithfully displays the memory location where this variable is stored. Aah, but the smart Alecs among you will say: ‘Hey, but int occupies 2 memory locations. So what will be displayed, the first memory location or the second?’ Good question!! Answer: the first memory location will be displayed. Thus, if we assume that i is stored at memory location 515,516 the above printf will display 515. Let us repeat, we are assuming i to be stored at location 515, it could be anything.

Sometimes, we just wonder, how is that the smallest of programs need the lengthiest explanations?? Its just uncanny.

 

Program 89

main()
{
char *i;
int *j;
long *h;
i=0;j=0;h=0;
printf("%p..%p..%p\n",i,j,h);
i++; j++; h++;
printf("%p..%p..%p\n",i,j,h);
i++; j++; h++;
printf("%p..%p..%p\n",i,j,h);
i++; j++; h++;
printf("%p..%p..%p\n",i,j,h);
}

Does this program remind you of some program we have encountered in the past?? Here we have 3 pointers i,j,h of data type char, int, long respectively. We are initializing the values of these 3 variables to 0. Just to check at the scenario, we have the first printf which tells us:

0000:0000..0000:0000..0000:0000

Then we go on to increment the value of each variable. Now, let us give you some food for thought: Pointers are of size 4. So, when we increment the values of these variables will they increase by 4?? The answer is No!! Why? All the 3 variables are pointers of different data type. Thus the next printf will prove to you that i, which is of type char is incremented by 1, j which is of type int is incremented by 2 and h, which is of type long, is incremented by 4. Thus the output of the second printf is:

0000:0001..0000:0002..0000:0004

Think we are kidding with you? Increment the variables once again and check their values with the third printf. The output is:

0000:0002..0000:0004..0000:0008

Thus, the combined output looks like this:

0000:0000..0000:0000..0000:0000
0000:0001..0000:0002..0000:0004
0000:0002..0000:0004..0000:0008

 

You all have heard of how in Karate, we have levels of expertise. They are called ‘belts’ and are given name of colors (why colors, we don’t know. Maybe it is something to do with the ‘syntax’ of karate). The more badly punch the guy, the higher your belt (and you thought C was weird!!). The next 3 programs that we will write are pretty interesting. If you understand them, consider yourself a ‘black belt’ in pointers. Ready to fight?. Lets go:

 

Program 90

main()
{
int i;
char *p;
printf("Memory location of i= %p\n",&i);
i=300;
printf("Value of i= %d\n",i);
printf("Memory location of p= %p\n",&p);
p=&i;
printf("Value of p= %p\n",p);
*p=10;
printf("Value of i= %d\n",i);
p++;
printf("Value of p= %p\n",p);
*p=10;
printf("Value of i= %d\n",i);
p++;
printf("Value of p= %p\n",p);
*p=100;
printf("Value of i= %d\n",i);
}

It is imperative that you follow this program, because the next two programs are similar. To help you understand the program really well, we have exploded the program into lines, explaining one line at a time.

Now if you are ready, we'll start:

Line 1 main()

You know this.

Line 2 {

Do you want us to repeat the same story …. again.

Line 3 int i;

We are reserving 2 locations in memory and calling them i. Two locations, since size of an int is 2. (Refer program 9). For the sake of understanding, we assume that the memory locations reserved are locations 100 and 101. Pictorially: 

Line 4 char *p;

Hmmm … interesting statement. p is a pointer. What does that mean? It means it represents 4 memory locations. Here to make our life simple we assume p occupies locations starting from 200. What does it also mean? It further means, that the value of p is treated as a computer memory location. Also, p is a pointer of data type char. The significance of this will be clear shortly.

Line 5 printf("%p\n",&i);

Tell yourself: " '&' means address of memory location of variable i. %p means that the output will be a computer memory location. This statement will display the actual address of memory location that is named i. But we have two memory locations under i. So what does this printf display? The first memory location: 100.

Line 6 i=300;

When C sees a value assigned to a variable, it first finds out the location of that variable in memory. In this case, when C sees 300, it looks for the location of I. It finds it be 100. It then divides 300 by 256. (why 256? Because in a single memory location we can only store values 0 to 255). The quotient is 1, the remainder is 44. This it then puts in the memory locations 100 and 101 as follows:

We are just making the value of i 300. Period.

Line 7 printf("%d\n",i);

Simple printf statement. We do not trust our computer to be responsible enough so we check to see if i is actually stored as 300. This statement will make you trust the computer more. The value displayed is indeed 300.

Line 8 printf("%p\n",&p);

Aha! Inquisitive, aren't we? We now want to know the address of p in memory. Your trusted companion (the computer) will faithfully tell you the where in memory does p get stored. Anything else worth noting?? Yes!! Don't you see a %p, indicating that the output is a computer memory location! People who are paying real attention to what we are saying would ask : "p is a pointer, hence it occupies 4 memory locations, but the size of char is 1, so isn’t this a contradiction?" Good question! Great Question!! Answer? Read on, there is light at the end of the tunnel.

Line 9 p=&i;

&i is a computer memory location! Right or wrong? Right! We also know (from Line 3) that p is a pointer, which means the value stored in it is understood as a computer memory location. "So what?" So, in this line we are simply putting the address of i into p.

 

Line 10 printf("%p\n",p);

Checking never hurt anyone. So why not be sure of what is happening! This printf will tell you exactly what the value of p is. Satisfied that it is 100, we take one more step.

Line 11 *p=10;

Wake up! Wake up!! Understand this right now or it will give you sleepless nights for a long time to come. This small statement packs quite a punch. Time to ask ourselves questions! What is p?? A variable which occupies 4 memory locations and whose value is understood by C as a computer memory location. What is the current value of p?? From line 9 we know it is 100. So when we type this line what happens?? The compiler runs to p (memory location 200) finds out its value (100), goes to memory location 100 and puts the value 10 in memory location 100. So what is effectively happening is:

*100=10

 We suggest you read the previous statements again and again and again … until it is absolutely clear.


Value of i: (2^8)*1 + (2^0)*10 = 256*1 + 1*10 = 266

We are not biologists but we can see that your gray cells are in a state of high activity. Good! Keep them that way! That’s the only way you will understand.

Line 12 printf("%d\n",i);

Checking time again. We ascertain if the value of i has really changed. This printf tells you it has become 266.

Line 13 p++;

 

You see the ++ sign after a variable and you say : " I know this one. You are incrementing the value of the variable." But don't stop at that. Ask: "By how much is the value being incremented?". Stop and ponder. Are you in a position to answer this question. Yes you are! How?? Fleet back to line 4. Remember, we defined p as a pointer of data type char. You know that size of char is one. (program 9). So, the value of p now becomes 101.

Line 14 printf("%p\n",p);

You know our habit. We make a change and check, if all is fine. This printf tells you all is indeed fine, and that the value of p is indeed 101.

Line 15 *p=10;

Now this should not be too difficult. Go to p (memory location 200). See what it contains (101). Go to that memory location and put the value 10. Effectively it means:

*101=10

Value of i becomes: (2^8)*10 + (2^0)*10 = 256*10 + 1*10 = 2560 + 10 = 2570

Line 16 p++

Similar to line 12, the value of p now becomes 102.

Line 17 printf("%p\n",p);

Checking to see the value of p. It is 102.

Line 18 *p=100;

Similar to lines 10 and 13 we now have:

*102=100

If you notice that we have been taking great pains in the diagrams to show you the position of i. Now, the time has come for you to know the significance of our labor. When we put 10 into memory location 102, do you think that i changes? One look at the diagram will tell you, NO! i represents the memory locations 100 101. What we do to other memory locations does not have any affect on i. So, the value of i is still 2570.

Line 19 printf("%d\n",i);

Just confirming the value of i. Guess what! Its still 2570.

Line 20 }

What a pretty sight. Just a close brace!! What could be better!

Actual Output:

Memory location of i= 1BD9:0FFE (we assumed it be 100)
Value of i= 300
Memory location of p= 1BD9:0FFA	(we assumed it to be 200)
Value of p= 1BD9:0FFE		(this is the location of i)
Value of i= 266		
Value of p= 1BD9:0FFF		(after first increment of p)
Value of i= 2570
Value of p= 1BD9:1000		(after second increment of p)
Value of i= 2570

 If you have understood the above program, you need to give yourself a treat. Go in front of the mirror and now the person you see is a transformed person. A person who understands pointers. A person who has proved that when the going gets tough, he/she gets going! Good work! Take a deep breath and try this program:

 Program 91

main()
{
int i;
int *p;
printf("Memory location of i= %p\n",&i);
i=300;
printf("Value of i= %d\n",i);
printf("Memory location of p= %p\n",&p);
p=&i;
printf("Value of p= %p\n",p);
*p=10;
printf("Value of i= %d\n",i);
p++;
printf("Value of p= %p\n",p);
*p=100;
printf("Value of i= %d\n",i);
}
 

"Another program. Another pointer program. Not fair!!" Relax! We have made just made one change in the program. Line 4 now says:

int *p;

instead of:

char *p;

We are intentionally copying the exact words of the explanation that we just gave you. (In fact even the wisecracks are the same). This should not be considered as a sign of lack of originality, but should be appreciated because now you can understand this program all by itself. No flipping pages. In other words, we have given you a "stand alone" explanation. Also, comparing programs is a lot easier.

Line 1 main()

You know this.

Line 2 {

Do you want us to repeat the same story …. again.

Line 3 int i;

We are reserving 2 locations in memory and calling them i. Two locations, since size of an int is 2. (Refer program 9). For the sake of understanding, we assume that the memory locations reserved are locations 100 and 101. Pictorially:

 

Line 4 int *p;

p is a pointer. What does that mean? It means it has 4 memory locations. Here to make our life simple we assume p occupies locations starting from 200. What does it also mean? It further means, that the value of this variable is treated as a computer memory location. Also, p is a pointer of data type int. The significance of this will be clear shortly.

Line 5 printf("%p\n",&i);

Tell yourself: " '&' means address of memory location of variable i. %p means that the output will be a computer memory location. This statement will display the actual address of memory location that is named i. But we have two memory locations under i. So what does this printf display? The first memory location: 100.

Line 6 i=300;

i is stored as follows: (2^8)*1 +(2^0)*44 = 256*1 + 1*44 = 300. Phew!!

We are just making the value of i 300. Period.

Line 7 printf("%d\n",i);

Simple printf statement. We do not trust our computer to be responsible enough so we check to see if i is actually stored as 300. This statement will make you trust the computer more. The value displayed is indeed 300. Understand another thing: whenever you want to call a variable you call it by its name. This causes the C compiler to go that memory location and pick or place the value according to what the statement asks it to do. So:

Variable name ------------------------ Memory Location

Translated to

Line 8 printf("%p\n",&p);

 Aha! Inquisitive, aren't we? We now want to know the address of p in memory. Your trusted companion (the computer) will faithfully tell you the where in memory does p get stored. Anything else that worth noting?? Yes!! Don't you see a %p, indicating that the output is a computer memory location! People who are paying real attention we are saying would ask : "p is a pointer, hence it occupies 4 memory locations, but the size of int is 2 so isn’t this a contradiction?" Good question! Great Question!! Answer? Read on, there is light at the end of the tunnel.

Line 9 p=&i;

&i is a computer memory location! Right or wrong? Right! We also know (from Line 3) that p is a pointer, which means the value stored in it is understood as a computer memory location. "So what?" So, in this line we are simply putting the address of i into p.  

Line 10 printf("%p\n",p);

Checking never hurt anyone. So why not be sure of what's happening! This printf will tell you exactly what the value of p is. Satisfied that it is 100, we take one more step.

Line 11 *p=10;

Wake up! Wake up!! Understand this right now or it will give you sleepless nights for a long time to come. This small statement packs quite a punch. Time to ask ourselves questions! What is p?? A variable which occupies 4 memory locations and whose value is understood by C as a computer memory location. What is the current value of p?? From line 9 we know it is 100. So when we type this line what happens?? The compiler runs to p (memory location 200) finds out its value (100), goes to memory location 100 and puts the value 10 in memory location 100. Now, the significance of Line 4:

int *p;

will be clear. Since p is a pointer of data type int, and we know that int has of size 2, the number 300 stored in i will now be replaced by 10.

So what is effectively happening is:

*100=10

Before Line 13: i is 300 After line 13: i is 10 (because of int *p)

 (Compare this with previous program)

The important thing to note here is that, since p is a pointer to a int, the value 10 is written to 2 memory locations, 100, 101 (since size of int is 2)

We suggest you read the previous statements again and again and again … until it is absolutely clear.

We are not biologists but we can see that your gray cells are in a state of high activity. Good! Keep them that way! That’s the only way you will understand.

Line 12 printf("%d\n",i);

Checking time again. We ascertain if the value of i has really changed. This printf tells you it has changed to 10.

Line 13 p++;

You see the ++ sign after a variable and you say : " I know this one. You are incrementing the value of the variable." But don't stop at that. Ask: "By how much is the value being incremented?". Stop and ponder. Are you in a position to answer this question. Yes you are! How?? Fleet back to line 4. Remember, we defined p as a pointer of data type int. You know that size of int is two. (program 9). So, the value of p now becomes 102.

Line 14 printf("%p\n",p);

You know our habit. We make a change and check to see if all is fine. This printf tells you all is fine, and that the value of p is indeed 102.

Line 15 *p=10; 

Now this should not be too difficult. Go to p (memory location 200). See what it contains (102). Go to that memory location and put the value 10. Effectively it means:

*102=100

Line 16 printf("%d\n",i)

If you notice that we have been taking great pains in the diagrams to show you the position of i. Now, the time has come for you to know the significance of our labor. When we put 10 into memory location 102, do you think that i changes? One look at the diagram will tell you, NO! i represents the memory locations 100 101. What we do to other memory locations does not have any affect on i. So, the value of i is still 10.

Line 17 }

Actual Output

Memory location of i= 1BDA:0FFE	(we assumed it to be 100)
Value of i= 300
Memory location of p= 1BDA:0FFA	(we assumed it be 200)
Value of p= 1BDA:0FFE		(this is the location of i)
Value of i= 10
Value of p= 1BDA:1000		(after first increment of p)
Value of i= 10
Value of p= 1BDA:1002		(after second increment of p)
Value of i= 10

Abnormal program termination (We have written to locations that do not belong to us, i.e. locations that have not been reserved by us. Writing to locations which have not been declared by us can lead to undesirable results. This is what we mean by the danger of pointers. One mistake and the computer might hang.)

"How are you feeling sir?" We just hope that the pointers in your brain haven't gone haywire. If you haven't followed the previous 2 examples sit with them, sleep over them, do whatever but get it absoluuutely clear. This is the last program in this marathon series. Jump right into it:

Program 92

main()
{
long i;
char *j;
int *k;
long *l;
printf("%p\n",&i);
i=65536+515;
printf("%ld\n",i);
printf("%p\n",&j);
j=&i;
printf("%p\n",j);
*j=10;
printf("%ld\n",i);
j++;
printf("%p\n",j);
*j=10;
printf("%ld\n",i);
i=65536+515;
k=&i;
printf("%p\n",k);
*k=10;
printf("%ld\n",i);
k++;
printf("%p\n",k);
*k=10;
printf("%ld\n",i);
i=65536+515;
l=&i;
printf("%p\n",l);
*l=10;
printf("%ld\n",i);
l++;
printf("%p\n",l);
*l=20;
printf("%ld\n",i);
}

Phew!! But, hey who is scared of long programs, anyway? We will assume here, that you have grown up from being 'pointer-kids' to 'pointer adults'. For kids we need break the dose into small doses, adults can take it all at once. So enough of spoon feeding lets, act like grown-ups.

Here we define i as a long, which means we are reserving 4 locations somewhere in memory and calling them i. We will assume that these locations begin from 100.

Then, we have a flood of pointers. j is a pointer to a char, k is a pointer to an int and l is a pointer to an long.

In the first printf we display the actual address of i. We have assumed it to be 100.

Then, i is given a value of 65536+515. Don't you dare our mathemetical prowess. We have left the addition incomplete because that way our representation of memory makes more sense. Thus:

Value of i= (2^16)*1 +(2^8)*2 + (2^0)*1 = 65536*1 + 256*2 + 1*3 = 66051

The next statement is:

printf("%ld\n",i);

Here we display the value of i. Notice that we have used '%ld' in the printf, since i is a long.

Now we check on j and find its location in memory. The next printf displays the memory location of j. Let us assume it to be 200.

In the next statement, we innnocently put the location of i into j. To check if the location of i has indeed reached i, we have the next printf, which displays the value of j.

The next statement is:

*j=10;

This statement is equivalent to saying:

*100=10;

Thus, we put the value 10 into memory location 100. Note, that since j is apointer to a char, the value 10 has gone into 1 memory location only. i.e 100. Now the memory location represented by i looks like this: 

Value of i= (2^16)*1 +(2^8)*2 + (2^0)*10 = 65536*1 + 256*2 + 1*10 = 66058

The value of i displayed in the next printf tells us this new value of i.

Next, we increment j. Again, the data type of the pointer (char) will ensure that the value of j is incremented only by 1. In the next printf we check the value of j after the increment and find it to be 101. Then we gain have the statmenet:

*j=10;

which now is like saying:

*101=10;

Thus, we put the value 10 into memory location 101. Note, that since j is apointer to a char, the value 10 has gone into 1 memory location only. i.e 101. Now the memory location represented by i looks like this:

Value of i= (2^16)*1 +(2^8)*10 + (2^0)*10 = 65536*1 + 256*10 + 1*10 = 68106

Thus i now has a new value which is displayed by the next printf.

Now, we make the value of i the same as what we started with, i.e 65536+515. We, then put the location of i into k. Thus, k now has a value 100. We display the value of k as a cross check.

Then, we have:

*k=10;

which is equivalent to saying:

*100=10;

Aah, but k is a pointer to an int. Thus, we are effectively putting the value 10 into 2 memory locations (size of int is 2), 100 and 101. Thus, a snaphot of the location i like this:

Value of i= (2^16)*1 +(2^8)*0 + (2^0)*10 = 65536*1 + 1*10 = 65546

The next printf tells us that the value has changed. Then, we increment k. Since k is of type int, incrementing k results in a jump of 2. Thus value of k now becomes 102. Then we again have the statement:

*k=10;

which is equivalent to:

*102=10;

This will result in change of memory locations 102 and 103, since k is a pointer to type int. Thus, location i will now look like this:

Value of i= (2^16)*10 +(2^8)*0 + (2^0)*10 = 65536*10 + 1*10 = 655370

The value of i is displayed by the printf.

Lie before, we make the value of i equal to 65536+515. Then we put the location of i into l. Thus, l now contains 100. Then we have the statement:

*l=10;

This means:

*100=10;

Now, l is a pointer to a long, thus the value 10 has gone into 4 memory locations. i.e 100,101,102,103. Now the memory location i looks like this:

Value of i= (2^16)*0 +(2^8)*0 + (2^0)*10 = 10

The printf gives us this new value of i.

Then we increment l. i is now incremented by 4, since it is a pointer to a long. Thus the value of i now becomes 104. Then, when we have the statement:

*i=20;

It is understood as:

*100=20;

20 is assigned locations 104, 105, 106, 107. Thus, location i looks like:

The value of i reamains unchanged since i represents locations 100, 101, 102 and 103. But we haveen't touched these memory ocations. Thus, i still has a value 10. But now, since we are writing into memory locations not reseved by us, there is always the danger of the computer hanging. (PS: when we ran the program the putput just would'nt stop scrolling)

Actual Output:

1BDE:0FFC	
66051			
1BDE:0FF8
1BDE:0FFC
66058
1BDE:0FFD
68106
1BDE:0FFC
65546
1BDE:0FFE
655370
1BDE:0FFC
10
1BDE:1000
The output of the last printf could not displayed

 

The other side of C

 In this section we will introduce you to a five letter word that will soon become a part of your life. It is a word, is it a concept? Its both! Its the stack. So what’s so special about the stack and what is made of? There are two components of the stack: the stack and the stack pointer. Stack is an area of memory which everybody knows about and can use. The stack pointer tells us the position of the stack. There is a list of things that the stack does. For now, we will look at only one aspect of the stack and that is: the stack acting as a facilitator in the exchange of parameters between functions. When one function calls another function, parameters have to be passed between the two functions. Its like 2 people meeting for the first time at the workplace. They have to work together, but they don’t know anything about the other. Some information has to be passed between the two, which tells each person about the other. Once this is done the two work better.

Before we start talking about the stack, we warn you that some of the things that happen on the stack, will simply stump you. Not because of the complexity, but on the contrary, because of the sheer stupidity of the whole concept. If after reading about the stack you say: "How can the stack be so stupid?", consider yourself a master of the stack. This is where you will realize that the awe that you had about C and its nitty gritty begin to vanish and the truth about how it really works comes to the fore.

We will treat the stack as a cake and learn it bite by bite (or should we say byte by byte). Remember, too much at one time, will only cause indigestion.

The first bite:

Program 93

main()
{
int i,j;
i=300;
j=515;
abc(I,j);
}
abc(x,y)
int x,y;
{
printf("%d..\n",x,y);
}

We wil use this program as the beacon for all our future function programs. So, easy does it. One line at a time.

main()

Here we create a function main().

{

indicates the start of main().

int i,j:

Here we reserve 2 memory locations for i and 2 memory locations for j, somewhere in memory. Let us assume that i is allocated 2 memory locations starting from 900 j is allocated 2 memory locations starting from 950. 

i=300;
j=515;

These statements are assignment statements where I and j are assigned values. C divides 300 by 256, puts the remainder (44) in 900 and the quotient (1) in 901. Similarly 515 is divided by 256, the remainder (3) is put in location 950 and the quotient is put into location 951.

abc(i,j);

 This is a call to functions abc(). How do we know? Because of the semicolon(;) at the end of the line. This function is called with 2 parameters i and j. Now, C does not like variables and hence replaces the variables i and j with their respective values. To find the value of i, C finds out its location in memory. It finds it to be 900. It runs to location 900, and picks the value of i. Similarly, C picks up the value of j from 950.

In calling functions, parameters have to be placed on the stack. We will assume that the stack points to location 104. When variables are pushed on the stack, the stack pointer moves backwards. Thus, main() puts j on the stack first. What locations will j occupy on the stack? Since j is an int, it has size 2 and hence j is put into locations 103 and 102. 

Now the stack pointer points to 102. Then I is pushed on the stack by main() at locations 101 and 100. The end result is that the stack pointer points to location 100.

Now, main () beats a graceful retreat and abc () enters the scence. abc() does not know what has been happening in the program. Thus, the first question abc() asks:`Pray tell me where is the stack pointer?’ 100 is the reply. abc() has very sharp eyes. It sees that within its () there are parameters x and y. Thus, abc() now knows that 2 numbers have to be removed from the s