Subscribe

RSS Feed (xml)

Powered By

Skin Design:
Free Blogger Skins

Powered by Blogger

Tuesday, March 25, 2008

Structure and Nesting






Structure and Nesting




Objectives


This section brings together the various looping mechanisms available to the
C programmer with the program control constructs we met in the last section.


We also demonstrates a neat trick with random numbers.




It is one of the great discoveries of programming that you can write any
program using just simple while loops and if

statements. You don't need any other control statements at all. Of course it
might be nice to include some other types of control statement to make life easy
- for example, you don't need the for loop, but it is good to
have! So as long as you understand the if and the while
loop in one form or another you can write any program you want to.


If you think that a loop and an if statement are not much to
build programs then you are missing an important point. It's not just the
statements you have, but the way you can put them together. You can include an if

statement within a loop, loops within loops are also OK, as are loops in ifs,
and ifs in ifs and so on. This putting one
control statement inside another is called nesting and it is really what
allows you to make a program as complicated as you like.




 


Think of a number


Now let's have a go at writing the following program: 'It thinks of a number
in the range 0 to 99 and then asks the user to guess it
'. This sounds
complicated, especially the 'thinks of a number' part, but all you need to know
is that the statement:

r = rand()


will store a random number in the integer variable r. The
standard library function rand() randomly picks a number within
the range 0 to 32767, but this might vary from machine to machine. Look upon rand()
as being a large dice.


Our problem is to select a number between 0 and 99 and not between 0 and
32767. How can we get our random number to within our range? The rand()
function will produce numbers such as:



2567
134
20678
15789
32001
15987
etc...


If you look at the last two digits of all of these numbers they would form
our random set! To select just these numbers we can use an arithmetic
calculation of the following form:


r = rand() % 100


That is, to get the number into the right range you simply take the remainder
on dividing by 100, ie a value in the range 0 to 99. You should remember this
neat programming trick, you'll be surprised how often it is required.


Our solution to the problem is as follows:



#include <stdio.h>

main()
{
int target;
int guess;
int again;

printf("\n Do you want to guess a number 1 =Yes, 0=No ");
scanf("%d",&again);

while (again)
{
target = rand() % 100;
guess = target + l;

while(target!=guess)
{
printf('\n What is your guess ? ");
scanf("%d",&guess);

if (target>guess) printf("Too low");
else printf("Too high");
}

printf("\n Well done you got it! \n");
printf("\nDo you want to guess a number 1=Yes, 0=No");
scanf("%d".&again);
}
}



[program]

This looks like a very long and complicated program, but it isn't.
Essentially it used two loops and an if/else which in English
could be summarised as:


 



while(again) {
think of a number
while (user hasn't guessed it)
{
get users guess.

if (target < guess) tell the user the guess is low
else tell the user the guess is high
}
}


The integer variable again is used to indicate that the user
wants to carry on playing. If it is 0 then the loop stops so 0 = No, and 1, or
any other non-zero value, = Yes.

If you try this program out you will discover that it has a slight flaw - not
so much a bug, more a feature. If the user guesses the correct value the program
still tells the user that the guess is too high and then congratulates them that
they have the correct value. Such problems with how loops end are common and you
have to pay attention to details such as this. There are a number of possible
solutions, but the most straight forward is to change the inner loop so that the
first guess is asked for before the loop begins. This shifts the test for the
loop to stop to before the test for a high or low guess:


 



#include <stdio.h>

main()
{
int target;
int guess;
int again;

printf("\n Do you want to guess a number 1 =Yes, 0=No ");
scanf("%d",&again);

while (again)
{
target = rand() % 100;

printf('\n What is your guess ? ");
scanf("%d",&guess);

while(target!=guess)
{
if (target>guess) printf("Too low");
else printf("Too high");
printf('\n What is your guess ? ");
scanf("%d",&guess);
}

printf("\n Well done you got it! \n");
printf("\n Do you want to guess a number 1=Yes, 0=No");
scanf("%d".&again);
}
}


[program]

If you want to be sure that you understand what is going on here, ask
yourself why the line:


guess = target + 1;


was necessary in the first version of the program and not in the second?











Read more!

Monday, March 24, 2008

Functions and Prototypes






Functions and
Prototypes





Objectives



Having read this section you should be able to:




  1. program using correctly defined C functions


  2. pass the value of local variables into your C
    functions






Functions - C's Building Blocks



Some programmers might consider it a bit early to introduce the C function - but we think you can't get to it soon enough.
It isn't a difficult idea and it is incredibly useful. You could
say that you only really start to find out what C
programming is all about when you start using functions.

C functions are the equivalent of what in other
languages would be called subroutines or
procedures. If you are familiar with another language you
also need to know that C only has functions, so don't
spend time looking for the definition of subroutines or
procedures - in C the function does everything!



A function is simply a chunk of C code (statements)
that you have grouped together and given a name. The value of
doing this is that you can use that "chunk" of code repeatedly
simply by writing its name. For example, if you want to create a
function that prints the word "Hello" on the
screen and adds one to variable called total then
the chunk of C code that you want to turn into a function
is just:




printf("Hello");
total = total + l;



To turn it into a function you simply wrap the code in a pair
of curly brackets to convert it into a single compound
statement
and write the name that you want to give it in
front of the brackets:






demo()
{
printf("Hello");
total = total + 1;
}



Don't worry for now about the curved brackets after the
function's name. Once you have defined your function you can use
it within a program:




main()
{
demo();
}



In this program the instruction demo (); is
entirely equivalent to writing out all of the statements in the
function. What we have done is to create an new C function
and this, of course, is the power of functions. When you are
first introduced to the idea of functions, or their equivalent in
other languages, it is easy to fall into the trap of thinking
that they are only useful when you want to use a block of code
more than once.


Functions are useful here but they have a more important
purpose. If you are creating a long program then functions allow
you to split it into "bite-sized" chunks which you can work on in
isolation. As every C programmer knows, "functions are
the building blocks of programs
."






Functions and Local Variables



Now that the philosophy session is over we have to return to the
details - because as it stands the demo function
will not work. The problem is that the variable
total isn't declared anywhere. A function is a
complete program sub-unit in its own right and you can declare
variables within it just as you can within the

main program. If you look at the
main program we have been using you will notice
it is in fact a function that just happens to be called "main"!
So to make demo work we have to add the
declaration of the variable total:


demo()
{
int total;
printf("Hello");
total=total+1;
}



Now this raises the question of where exactly
total is a valid variable. You can certainly use

total within the function that declares it - this
much seems reasonable - but what about other functions and, in
particular, what about the main program? The
simple answer is that total is a variable that
belongs to the demo function. It cannot be used
in other functions, it doesn't even exist in other functions and
it certainly has nothing to do with any variable of the same name
that you declare within other functions.



This is what we hinted at when we said that functions were
isolated chunks of code. Their isolation is such that
variables declared within the function can only be used within
that function. These variables are known as local
variables
and as their name suggests are local to the
function they have been declared in. If you are used to a
language where every variable is usable all the time this might
seem silly and restrictive - but it isn't. It's what makes it
possible to break a large program down into smaller and more
manageable chunks.



The fact that total is only usable within the
demo function is one thing - but notice we said
that it only existed within this function, which is a more subtle
point. The variables that a function declares are created when
the function is started and destroyed when the function is
finished. So if the intention is to use total to
count the number of times the >demo function is
used - forget it! Each time demo is used the
variable total is created afresh, and at the end
of the function the variable goes up in a puff of smoke along
with its value. So no matter how many times you run demo
total
will only ever reach a value of 1, assuming that
it's initialised to 0.






Making The Connections



Functions are isolated, and whats more nothing survives after
they have finished. Put like this a function doesn't seem to be
that useful because you can't get data values in, you can't get
data values out, and they don't remember anything that happens to
them!

To be useful there has to be a way of getting data into and
out of a function, and this is the role of the curved brackets.
You can define special variables called parameters which
are used to carry data values into a function. Parameters are
listed and declared in between the () brackets in the function's
definition. For example:




sum( int a, int b)
{
int result;
result=a + b;
}



defines a function called sum with two parameters
a and b, both integers.
Notice that the result variable is declared in the usual way
within the body of the function. Also, notice that the parameters
a and b are used within the
function in the same way as normal variables - which indeed they
are. What is more, they are still local variables

and have nothing at all to do with any variables called
a and b defined in any other
function.

The only way in which parameters are any different is that you
can give them initial values when the function starts by writing
the values between the round brackets. So



sum(l,2);



is a call to the sum function with

a set to 1 and b set to 2 and so
result is set to 3. You can also initialise
parameters to the result of expressions such as:



sum(x+2,z*10);



which will set a equal to whatever

x+2 works out to be and b equal
to whatever z*10 works out to be.



As a simpler case you can also set a parameter to the value in
a single variable - for example:



sum(x,y);



will set a to the value stored in
x and b to the value stored in
y.



Parameters are the main way of getting values into a function,
but how do we get values out? There is no point in expecting the
>result variable to somehow magically get its
value out of the sum function - after all, it is
a local variable and is destroyed when sum

is finished. You might try something like:





sum(int a, int b, int result)
{
int result;
result = a + b;
}



but it doesn't work. Parameters are just ordinary variables that
are set to an initial value when the function starts running -
they don't pass values back to the program that used the
function. That is:




sum(l,2,r);



doesn't store 1+2 in r because the value in

r is used to initialise the value in
result and not vice versa. You can even try



sum(l,2,result);



and it still will not work - the variable
result within the function has nothing to do with
the variable result used in any other
program.



The simplest way to get a value out of a function is to use
the return instruction. A function can return a
value via its name - it's as if the name was a variable and had a
value. The value that is returned is specified by the
instruction:



return value;



which can occur anywhere within the function, not just as the
last instruction - however, a return always
terminates the function and returns control back to the calling
function. The only complication is that as the function's name is
used to return the value it has to be given a data type.
This is achieved by writing the data type in front of the
function's name. For example:



int sum(a,b);



So now we can at last write the correct version of the
sum function:




int sum(int a, int b)
{
int result;
result = a + b;
return result;
}



and to use it you would write something like:



r=sum(1,2);



which would add 1 to 2 and store the result in
r. You can use a function anywhere that you can
use a variable. For example,



r=sum(1,2)*3



is perfectly OK, as is



r=3+sum(1,2)/n-10



Obviously, the situation with respect to the number of
inputs and outputs of a function isn't equal. That
is you can create as many parameters as you like but a function
can return only a single value. (Later on we will
have to find ways of allowing functions to return more than one
value.)



So to summarise: a function has the general form:





type FunctionName(type declared parameter list)
{
statements that make up the function
}



and of course a function can contain any number of
return statements to specify its return value and
bring the function to an end.

There are some special cases and defaults we need to look at
before moving on. You don't have to specify a parameter list if
you don't want to use any parameters - but you still need the
empty brackets! You don't have to assign the function a type in
which case it defaults to int. A function doesn't
have to return a value and the program that makes use of a
function doesn't have to save any value it does return. For
example, it is perfectly OK to use:



sum(1,2);



which simply throws away the result of adding 1 to 2. As this
sort of thing offends some programmers you can use the data type

void to indicate that a function doesn't return a
value. For example:



void demo();



is a function with no parameters and no return value.



void is an ANSI C standard data
type.



The break statement covered in a previous
section can be used to exit a function. The break
statement is usually linked with an if statement
checking for a particular value. For example:



if (x==1) break;



If x contained 1 then the
fuction would exit and return to the calling program.






Functions and Prototypes



Where should a function's definition go in relation to the entire
program - before or after main()? The only
requirement is that the function's type has to be known
before it is actually used. One way is to place the function
definition earlier in the program than it is used - for example,
before main(). The only problem is that most C
programmers would rather put the main program at the top
of the program listing. The solution is to declare the function
separately at the start of the program. For example:





int sum();
main()
{
etc...



declares the name sum to be a function that
returns an integer. As long as you declare functions
before they are used you can put the actual definition anywhere
you like.

By default if you don't declare a function before you use it
then it is assumed to be an int function - which
is usually, but not always, correct. It is worth getting into the
habit of putting function declarations at the start of your
programs because this makes them easier to convert to full
ANSI C.






What is ANSI C?



When C was first written the standard was set by its
authors Kernighan and Ritche - hence "K&R C". In 1990, an
international ANSI
standard for C was established which differs from K&AMPR C
in a number of ways.

The only really important difference is the use of function
prototypes. To allow the compiler to check that you are using
functions correctly ANSI C allows you to include a
function prototype which gives the type of the function
and the type of each parameter before you define the function.
For example, a prototype for the sum function
would be:



int sum(int,int);



meaning sum is an int function
which takes two int parameters. Obviously, if you
are in the habit of declaring functions then this is a small
modification. The only other major change is that you can declare
parameter types along with the function as in:





int sum(int a, int b);
{



rather than:


int sum(a,b)
int a,b;
{



was used in the original K&R C. Again, you can see that
this is just a small change. Notice that even if you are using an
ANSI compiler you don't have to use prototypes and the K&R
version of the code will work perfectly well.






The Standard Library Functions



Some of the "commands" in C are not really "commands" at
all but are functions. For example, we have been using
printf and scanf to do input and
output, and we have used rand to generate random
numbers - all three are functions.

There are a great many standard functions that are included
with C compilers and while these are not really part of
the language, in the sense that you can re-write them if you
really want to, most C programmers think of them as
fixtures and fittings. Later in the course we will look into the
mysteries of how C gains access to these standard
functions and how we can extend the range of the standard
library. But for now a list of the most common libraries and a
brief description of the most useful functions they contain
follows:




  • stdio.h: I/O functions:


    • getchar() returns the next character typed on the
      keyboard.


    • putchar() outputs a single character to the
      screen.


    • printf() as previously described


    • scanf() as previously described




  • string.h: String functions


    • strcat() concatenates a copy of str2 to str1


    • strcmp() compares two strings


    • strcpy() copys contents of str2 to str1




  • ctype.h: Character functions


    • isdigit() returns non-0 if arg is digit 0 to 9


    • isalpha() returns non-0 if arg is a letter of the
      alphabet


    • isalnum() returns non-0 if arg is a letter or
      digit


    • islower() returns non-0 if arg is lowercase
      letter


    • isupper() returns non-0 if arg is uppercase
      letter




  • math.h: Mathematics functions


    • acos() returns arc cosine of arg


    • asin() returns arc sine of arg


    • atan() returns arc tangent of arg


    • cos() returns cosine of arg


    • exp() returns natural logarithim e


    • fabs() returns absolute value of num


    • sqrt() returns square root of num





  • time.h: Time and Date functions


    • time() returns current calender time of system


    • difftime() returns difference in secs between two
      times


    • clock() returns number of system clock cycles since
      program execution





  • stdlib.h:Miscellaneous functions


    • malloc() provides dynamic memory allocation, covered
      in future sections


    • rand() as already described previously


    • srand() used to set the starting point for rand()










Throwing The Dice



As an example of how to use functions, we conclude this section
with a program that, while it isn't state of the art, does show
that there are things you can already do with C. It also
has to be said that some parts of the program can be written more
neatly with just a little more C - but that's for later.
All the program does is to generate a random number in the range
1 to 6 and displays a dice face with the appropriate pattern.

The main program isn't difficult to write because we are going
to adopt the traditional programmer's trick of assuming that any
function needed already exists. This approach is called
stepwise refinement, and although its value as a
programming method isn't clear cut, it still isn't a bad way of
organising things:





main()
{
int r;
char ans;

ans = getans();

while(ans== 'y')
{
r = randn(6);
blines(25);
if (r==1) showone();
if (r==2) showtwo();
if (r==3) showthree();
if (r==4) showfour();
if (r==5) showfive();
if (r==6) showsix();
blines(21);
ans = getans();
}

blines(2);
}



If you look at main() you might be a bit
mystified at first. It is clear that the list of
if statements pick out one of the functions
showone, showtwo etc. and so
these must do the actual printing of the dot patterns - but what
is blines, what is getans and why
are we using randn()? The last time we used a
random number generator it was called rand()!



The simple answers are that blines(n) will
print n blank lines, getans()
asks the user a question and waits for the single letter answer,
and randn(n) is a new random number generator
function that produces a random integer in the range 1 to
n - but to know this you would have written the
main program. We decided what functions would make our task
easier and named them. The next step is to write the code to fill
in the details of each of the functions. There is nothing to stop
me assuming that other functions that would make my job easier
already exist. This is the main principle of stepwise
refinement
- never write any code if you can possibly invent
another function! Let's start with randn().



This is obviously an int function and it can
make use of the existing rand() function in the
standard library




int randn(int n)
{
return rand()%n + 1;
}



The single line of the body of the function just returns the
remainder of the random number after dividing by
n - % is the remainder operator -
plus 1. An alternative would be to use a temporary variable to
store the result and then return this value. You can also use
functions within the body of other functions.



Next getans()




char getans()
{
int ans;

printf("Throw y/n ?");
ans = -1;
while (ans == -1)
{
ans=getchar();
}
return ans;
}



This uses the standard int function
getchar() which reads the next character from the
keyboard and returns its ASCII code or -1 if there isn't a key
pressed. This function tends to vary in its behaviour according
to the implementation you are using. Often it needs a carriage
return pressed before it will return anything - so if you are
using a different compiler and the program just hangs, try
pressing "y" followed the by Enter or Return

key.



The blines(n) function simply has to use a
for loop to print the specified number of
lines:




void blines(int n)
{
int i;

for(i=1 ; i<=n ; i++) printf("\n");
}



Last but not least are the functions to print the dot
patterns. These are just boring uses of printf to
show different patterns. Each function prints exactly three lines
of dots and uses blank lines if necessary. The reason for this is
that printing 25 blank lines should clear a standard text screen
and after printing three lines printing 21 blank lines will
scroll the pattern to the top of the screen. If this doesn't
happen on your machine make sure you are using a 29 line text
mode display.




void showone()
{
printf("\n * \n");
}

void showtwo()
{
printf(" * \n\n");
printf(" * \n");
}

void showthree()
{
printf(" * \n");
printf(" * \n");
printf(" *\n");
}

void showfour()
{
printf(" * * \n\n");
printf(" * * \n");
}

void showfive()
{
printf(" * * \n");
printf(" * \n");
printf(" * * \n");
}

void showsix()
{
int i;

for(i=1 ; i>=3 ; i++) printf(" * * \n");
}



The only excitement in all of this is the use of a
for loop in showsix! Type this
all in and add:




void showone();
void showtwo();
void showthree();
void showfour();
void showfive();
void showsix();
int randn();
char getans();
void blines();



before the main function if you type the other
functions in after.



[program]



Once you have the program working try modifying it. For
example, see if you can improve the look of the patterns. You
might also see if you can reduce the number of
showx functions in use - the key is that
the patterns are built up of combinations of two horizontal dots
and one centred dot. Best of luck.












Read more!

File Handling





File Handling





Objectives



So far we have entered information into our programs via the
computer's keyboard. This is somewhat laborious if we have a lot
of data to process. The solution is to combine all the input data
into a file and let our C program read the information
when it is required.



Having read this section you should be able to:




  1. open a file for reading or writing


  2. read/write the contents of a file


  3. close the file






The Stream File



Although C does not have any built-in method of performing
file I/O, the C standard library contains a very rich set
of I/O functions providing an efficient, powerful and flexible
approach. We will cover the ANSI file system but it must be
mentioned that a second file system based upon the original UNIX
system is also used but not covered on this course.


A very important concept in C is the
stream. In C, the stream
is a common, logical interface to the various devices that
comprise the computer. In its most common form, a
stream is a logical interface to a
file. As C defines the term "file", it can
refer to a disk file, the screen, the keyboard, a port, a file on
tape, and so on. Although files differ in form and capabilities,
all streams are the same. The
stream provides a consistent interface and to the
programmer one hardware device will look much like another.



A stream is linked to a file using an
open operation. A stream is
disassociated from a file using a close
operation
. The current location, also referred to as the
current position, is the location in a file where the next file
access will occur. There are two types of streams:
text
(used with ASCII characters some character
translation takes place, may not be one-to-one correspondence
between stream and what's in the file) and binary

(used with any type of data, no character translation, one-to-one
between stream and file).



To open a file and associate it with a stream,
use fopen(). Its prototype is shown here:



FILE *fopen(char *fname,char *mode);



The fopen() function, like all the file-system
functions, uses the header stdio.h . The name of
the file to open is pointed to by fname (must be a
valid name). The string pointed at for mode

determines how the file may be accesed as shown:























ModeMeaning
r Open a text file for reading
w Create a text file for writing
a Append to a text file
rb Open a binary file for reading
wb Open a binary file for writing
ab Append to a binary file
r+ Open a text file for read/write
w+ Create a text file for read/write
a+ Append or create a text file for read/write
r+b Open a binary file for read/write
w+b Create a binary file for read/write
a+b Append a binary file for read/write


If the open operation is successful, fopen()
returns a valid file pointer. The type

FILE is defined in stdio.h. It is a
structure that holds various kinds of information about the file,
such as size.The file pointer will be used with
all other functions that operate on the file and it must never be
altered or the object it points to. If fopen()
fails it returns a NULL pointer so this must
always be checked for when opening a file. For example:





FILE *fp;

if ((fp = fopen("myfile", "r")) ==NULL){
printf("Error opening file\n");
exit(1);
}



To close a file, use fclose(), whose prototype is

int fclose(FILE *fp);



The fclose() function closes the file
associated with fp, which must be a valid file
pointer
previously obtained using

fopen(), and disassociates the
stream from the file. The
fclose() function returns 0 if successful and
EOF (end of file) if an error occurs.



Once a file has been opened, depending upon its mode, you may
read and/or write bytes to or from it using these two
functions.





int fgetc(FILE *fp);
int fputc(int ch, FILE *fp);



The getc() function reads the next byte from the
file and returns its as an integer and if error occurs returns
EOF. The getc() function also
returns EOF when the end of file is reached. Your
routine can assign fget()'s return value to a

char you don't have to assign it to an integer.

The fput() function writes the bytes contained
in ch to the file associated with fp as an unsigned
char. Although ch is defined as an

int, you may call it using simply a
char. The fput() function returns
the character written if successful or EOF if an
error occurs.






Text File Functions



When working with text files, C provides four
functions which make file operations easier. The first two are
called fputs() and fgets(), which
write or read a string from a file, respectively. Their
prototypes are:




int fputs(char *str,FILE *fp);
char *fgets(char *str, int num, FILE *fp);



The fputs() function writes the string pointed to
by str to the file associated with fp. It returns

EOF if an error occurs and a non-negative value
if successful. The null that terminates str is not written
and it does not automatically append a carriage return/linefeed
sequence.

The fget() function reads characters from the
file associated with fp into a string pointed to by
str until num-1 characters have been read, a new line character is encountered, or the end of the file is reached. The
string is null-terminated and the new line character is retained.
The function returns str if successful and a null
pointer
if an error occurs.



The other two file handling functions to be covered are
fprintf() and fscanf(). These
functions operate exactly like printf() and
scanf() except that they work with files. Their
prototypes are:






int fprintf(FILE *fp, char *control-string, ...);
int fscanf(FILE *fp, char *control-string ...);



Instead of directing their I/O operations to the console, these
functions operate on the file specified by fp. Otherwise
their operations are the same as their console-based relatives.
The advantages to fprintf() and
fscanf() is that they make it very easy to write
a wide variety of data to a file using a text format.




Binary File Functions



The C file system includes two important functions:
fread() and fwrite(). These
functions can read and write any type of data, using any kind of
representation. Their prototypes are:




size_t fread(void *buffer, size_t size, size_t num,FILE *fp);
size_t fwrite(void *buffer, size_t size, size_t num, FILE *fp);



The fread() function reads from the file
associated with fp, num number of objects, each
object size bytes long, into buffer pointed to by buffer.
It returns the number of objects actually read. If this value is
0, no objects have been read, and either end of file has been
encountered or an error has occurred. You can use

feof() or ferror() to find out
which. Their prototypes are:




int feof(FILE *fp);
int ferror(FILE *fp);



The feof() function returns non-0 if the file
associated with fp has reached the end of file, otherwise
it returns 0. This function works for both binary files and text
files. The ferror() function returns non-0 if the
file associated with fp has experienced an error,
otherwise it returns 0.


The fwrite() function is the opposite of
fread(). It writes to file associated with
fp, num number of objects, each object size bytes
long, from the buffer pointed to by buffer. It returns the
number of objects written. This value will be less than
num only if an output error as occurred.



The void pointer is a pointer that can point
to any type of data without the use of a TYPE cast (known as a
generic pointer). The type size_t is a variable
that is able to hold a value equal to the size of the largest
object surported by the compiler. As a simple example, this
program write an integer value to a file called MYFILE using its
internal, binary representation.




#include <stdio.h> /* header file */
#include <stdlib.h>

void main(void)
{

FILE *fp; /* file pointer */
int i;

/* open file for output */
if ((fp = fopen("myfile", "w"))==NULL){
printf("Cannot open file \n");
exit(1);
}
i=100;

if (fwrite(&i, 2, 1, fp) !=1){
printf("Write error occurred");
exit(1);
}
fclose(fp);

/* open file for input */
if ((fp =fopen("myfile", "r"))==NULL){
printf("Read error occurred");
exit(1);
}
printf("i is %d",i);
fclose(fp);
}



[program]




File System Functions



You can erase a file using remove(). Its
prototype is

int remove(char *file-name);



You can position a file's current location to the start of the
file using rewind(). Its prototype is



void rewind(FILE *fp);



Hopefully I have given you enough information to at least get
you started with files. Its really rather easy once you get
started.






Command Line Parameters



Many programs allow command-line arguments to be specified when
they are run. A command-line argument is the information that
follows the program's name on the command line of the operating
system. Command-line arguments are used to pass information to
the program. For example, when you use a text editor, you
probably specify the name of the file you want to edit after the
name of the word processing program. For example, if you use a
word processor called WP, then this line causes the file TEST to
be edited.

WP TEST



Here, TEST is a command-line argument. Your C programs
may also utilize command-line arguments. These are passed to a C program through two arguments to the
main() function. The parameters are called
argc and argv. These
parameters are optional and are not used when no command-line
arguments are being used.



The argc parameter holds the number of
arguments on the command-line and is an integer. It will always
be at least 1 because the name of the program qualifies as the
first argument. The argv parameter is an array of
string pointers. The most common method for declaring

argv is shown here.



char *argv[];



The empty brackets indicate that it is an
array of undetermined length. All command-line
arguments are passed to main() as strings. To
access an individual string, index argv. For
example, argv[0] points to the program's name and

argv[1] points to the first argument. This
program displays all the command-line arguments that it is called
with.






#include <stdio.h>

void main(int argc, char *argv[])
{
int i;

for (i=1; i&ltargc; i++) printf("%s",argv[i]);
}



The ANSI C standard does not specify what constitutes a
command-line argument, because operatring systems vary
considerably on this point. However, the most common convention
is as follows:

Each command-line argument must be separated by a space or a
tab character. Commas, semicolons, and the like are not
considered separators. For example:



This is a test



is made up of four strings, but



this,that,and,another



is one string. If you need to pass a command-line argument
that does, in fact contain spaces, you must place it between
quotes, as shown in this example:



"this is a test"



A further example of the use of argc and
argv now follows:




void main(int argc, char *argv[])
{
if (argc !=2) {
printf("Specify a password");
exit(1);
}
if (!strcmp(argv[1], "password"))
printf("Access Permitted");
else
{
printf("Access denied");
exit(1);
}
program code here ......
}



This program only allows access to its code if the correct
password is entered as a command-line argument. There are many
uses for command-line arguments and they can be a powerful tool.

My final example program takes two command-line arguments. The
first is the name of a file, the second is a character. The
program searches the specified file, looking for the character.
If the file contains at least one of these characters, it reports
this fact. This program uses argv to access the
file name and the character for which to search.






/*Search specified file for specified character. */

#include <stdio.h>
#include <stdlib.h>

void main(int argc, char *argv[])
{
FILE *fp; /* file pointer */
char ch;

/* see if correct number of command line arguments */
if(argc !=3) {
printf("Usage: find <filename> <ch>\n");
exit(1);
}

/* open file for input */
if ((fp = fopen(argv[1], "r"))==NULL) {
printf("Cannot open file \n");
exit(1);
}

/* look for character */
while ((ch = getc(fp)) !=EOF) /* where getc() is a */
if (ch== *argv[2]) { /*function to get one char*/
printf("%c found",ch); /* from the file */
break;
}
fclose(fp);
}




[program]

The names of argv and argc are
arbitrary - you can use any names you like. However,
argc and argv have traditionally
been used since C's origin. It is a good idea to use these
names so that anyone reading your program can quickly identify
them as command-line parameters.








Read more!

Data Types Part II






Data Types Part II




Objectives


So far we have looked at local variable now we switch our attention to
other types of variables supported by the C programming language:



  1. Global Variables


  2. Constant Data Types





Global variables



Variables can be declared as either local variables which can be used
inside the function it has been declared in (more on this in further sections)
and global variables which are known throughout the entire program. Global
variables
are created by declaring them outside any function. For example:




int max;

main()
{
.....
}
f1()
{
.....
}


The int max can be used in both main and function f1 and any
changes made to it will remain consistent for both functions. The understanding
of this will become clearer when you have studied the section on functions but I
felt I couldn't complete a section on data types without mentioning global
and local variables.




Constant Data Types



Constants refer to fixed values that may not be altered by the program.
All the data types we have previously covered can be defined as constant data
types
if we so wish to do so. The constant data types must be defined
before the main function. The format is as follows:

#define CONSTANTNAME value


for example:



#define SALESTAX 0.05


The constant name is normally written in capitals and does not have a
semi-colon at the end. The use of constants is mainly for making your
programs easier to be understood and modified by others and yourself in the
future. An example program now follows:



#define SALESTAX 0.05
#include <stdio.h>
main()
{
float amount, taxes, total;
printf("Enter the amount purchased : ");
scanf("%f",&amount);
taxes = SALESTAX*amount;
printf("The sales tax is £%4.2f",taxes);
printf("\n The total bill is £%5.2f",total);
}



The float constant SALESTAX is defined with value 0.05. Three
float variables are declared amount, taxes
and total. Display message to the screen is achieved using printf

and user input handled by scanf. Calculation is then performed
and results sent to the screen. If the value of SALESTAX alters
in the future it is very easy to change the value where it is defined rather
than go through the whole program changing the individual values separately, which
would be very time consuming in a large program with several references. The
program is also improved when using constants rather than values as it improves
the clarity.












Read more!

Arrays







Arrays




Objectives


Having read this section you should have a good understanding of the use of
arrays in C.




 


Advanced Data Types


Programming in any language takes a quite significant leap forwards as soon as
you learn about more advanced data types - arrays and strings
of characters
. In C there is also a third more general and even more
powerful advanced data type - the pointer but more about that
later. In this section we introduce the array, but the first
question is, why bother?

There are times when we need to store a complete list of numbers or
other data items. You could do this by creating as many individual variables as
would be needed for the job, but this is a hard and tedious process. For
example, suppose you want to read in five numbers and print them out in reverse
order. You could do it the hard way as:



main()
{
int al,a2,a3,a4,a5;
scanf("%d %d %d %d %d",&a1,&a2,&a3,&a4,&a5);
printf("%d %d %d %d %d'',a5,a4,a3,a2,a1);
}


Doesn't look very pretty does it, and what if the problem was to read in 100
or more values and print them in reverse order? Of course the clue to the
solution is the use of the regular variable names a1, a2

and so on. What we would really like to do is to use a name like a[i]
where i is a variable which specifies which particular value we
are working with. This is the basic idea of an array and nearly all
programming languages provide this sort of facility - only the details alter.


In the case of C you have to declare an array before you use it
- in the same way you have to declare any sort of variable. For example,


int a[5];



declares an array called a with five elements. Just to
confuse matters a little the first element is a[0] and the last a[4].
C programmer's always start counting at zero! Languages vary according to
where they start numbering arrays. Less technical, i.e. simpler, languages start
counting from 1 and more technical ones usually start counting from 0. Anyway,
in the case of C you have to remember that


type array[size]


declares an array of the specified type and with size
elements. The first array element is array[0]

and the last is array[size-1].


Using an array, the problem of reading in and printing out a
set of values in reverse order becomes simple:




main()
{
int a[5];
int i;
for(i =0;i < 5; ++i) scanf("%d",&a[i]);
for(i =4;i> =0;--i) printf("%d",a[i]);
}



[program]


Well we said simple but I have to admit that the pair of for
loops looks a bit intimidating. The for loop and the array data
type were more or less made for each other. The for loop can be
used to generate a sequence of values to pick out and process each element in an
array in turn. Once you start using arrays, for loops like:



for (i=0 ; i<5 ; ++i)


to generate values in the order 0,1,2 and so forth, and


for(i=4;i>=0;--i)


to generate values in the order 4,3,2... become very familiar.




In Dis-array


An array of character variables is in no way different from an array of numeric
variables, but programmers often like to think about them in a different way.
For example, if you want to read in and reverse five characters you could use:

main()
{
char a[5];
int i;
for(i=0; i<5; ++i) scanf("%c",&a[i]);
for(i=4;i>=0;--i) printf("%c",a[i]);
}



Notice that the only difference, is the declared type of the array and the %c
used to specify that the data is to be interpreted as a character in scanf
and printf. The trouble with character arrays is that to use
them as if they were text strings you have to remember how many characters they
hold. In other words, if you declare a character array 40 elements long and
store H E L L O in it you need to remember that after element 4 the array is
empty. This is such a nuisance that C uses the simple convention that the
end of a string of characters is marked by a null character. A null
character is, as you might expect, the character with ASCII code 0. If you want
to store the null character in a character variable you can use
the notation \0 - but most of the time you don't have to
actually use the null character. The reason is that C will
automatically add a null character and store each character in a
separate element when you use a string constant. A string constant is indicated
by double quotes as opposed to a character constant which is indicated by a
single quote. For example:



"A"


is a string constant, but


'A'


is a character constant. The difference between these two superficially
similar types of text is confusing at first and the source of many errors. All
you have to remember is that "A" consists of two characters, the
letter A followed by \0 whereas 'A' is just the
single character A. If you are familiar with other languages you might think
that you could assign string constants to character arrays and work as if a
string was a built-in data type. In C however the fundamental data type
is the array and strings are very much grafted on. For example, if
you try something like:




char name[40];
name="Hello"


it will not work. However, you can print strings using printf
and read them into character arrays using scanf. For example,



main()
{

static char name[40] ="hello";

printf("%s",name);
scanf("%s",name);
printf("%s",name);
}


[program]



This program reads in the text that you type, terminating it with a null
and stores it in the character array name. It then prints the
character array treating it as a string, i.e. stopping when it hits the first null
string. Notice the use of the "%s" format descriptor
in scanf and printf to specify that what is
being printed is a string.



At this point the way that strings work and how they can be made a bit more
useful and natural depends on understanding pointers which is
covered in the next section.










Read more!

Pointers






Pointers





Objectives



Having read this section you should be able to:




  1. program using pointers


  2. understand how C uses pointers with arrays






Point to Point



Pointers are a very powerful, but primitive facility contained in
the C language. Pointers are a throwback to the days of
low-level assembly language programming and as a result they are
sometimes difficult to understand and subject to subtle and
difficult-to-find errors. Still it has to be admitted that
pointers are one of the great attractions of the C
language and there will be many an experienced C
programmer spluttering and fuming at the idea that we would dare
to refer to pointers as 'primitive'!

In an ideal world we would avoid telling you about pointers
until the very last minute, but without them many of the simpler
aspects of C just don't make any sense at all. So, with
apologies, let's get on with pointers.



A variable is an area of memory that has been
given a name. For example:



int x;



is an area of memory that has been given the name
x. The advantage of this scheme is that you can
use the name to specify where to store data. For example:



x=lO;



is an instruction to store the data value 10
in the area of memory named x. The variable is
such a fundamental idea that using it quickly becomes second
nature, but there is another way of working with memory.



The computer access its own memory not by using variable names
but by using a memory map with each location of memory uniquely
defined by a number, called the address of that memory
location.



A pointer is a variable that stores this location of memory.
In more fundamental terms, a pointer stores the address of
a variable . In more picturesque terms, a pointer points to a
variable.



A pointer has to be declared just like any other variable -
remember a pointer is just a variable that stores an address. For
example,



int *p;



is a pointer to an integer. Adding an asterisk in front of a
variable's name declares it to be a pointer to the declared type.
Notice that the asterisk applies only to the single variable name
that it is in front of, so:



int *p , q;



declares a pointer to an int and an
int variable, not two pointers.



Once you have declared a pointer variable you can begin using
it like any other variable, but in practice you also need to know
the meaning of two new operators: & and

*. The & operator returns the
address of a variable. You can remember this easily because
& is the 'A'mpersand character and it gets
you the 'A'ddress. For example:



int *p , q;



declares p, a pointer to int,
and q an int and the
instruction:



p=&q;



stores the address of q in p.
After this instruction you can think of p as
pointing at q. Compare this to:



p=q;



which attempts to store the value in q in the
pointer p - something which has to be considered
an error.



The second operator * is a little more
difficult to understand. If you place * in front
of a pointer variable then the result is the value stored in the
variable pointed at. That is, p stores the

address, or pointer, to another variable and
*p is the value stored in the variable
that p points at.



The * operator is called the
de-referencing operator and it helps not to confuse it with
multiplication or with its use in declaring a pointer.



This multiple use of an operator is called operator
overload
.



Confused? Well most C programmers are confused when
they first meet pointers. There seems to be just too much to take
in on first acquaintance. However there are only three basic
ideas:




  1. To declare a pointer add an * in front of its
    name.


  2. To obtain the address of a variable us & in
    front of its name.


  3. To obtain the value of a variable use * in
    front of a pointer's name.




Now see if you can work out what the following means:




int *a , b , c;
b = 10;
a = &b;
c = *a;



Firstly three variables are declared - a (a
pointer to int), and b and

c (both standard integers). The instruction
stores the value l0 in the variable b in the usual
way. The first 'difficult' instruction is a=&b
which stores the address of b in
a. After this a points to

b.



Finally c = *a stores the value in the varable
pointed to by a in c. As
a points to b, its value i.e. 1O
is stored in c. In other words, this is a long
winded way of writing



c = b;



Notice that if a is an int and
p is a pointer to an int then



a = p;



is nonsense because it tries to store the address of an
int, i.e. a pointer value, in an
int. Similarly:



a = &p;



tries to store the address of a pointer variable
in a and is equally wrong! The only assignment
between an int and a pointer to
int that makes sense is:



a = *p;






Swap Shop



At the moment it looks as if pointers are just a complicated way
of doing something we can already do by a simpler method.
However, consider the following simple problem - write a function
which swaps the contents of two variables. That is, write
swap(a,b) which will swaps over the contents of
a and b. In principle this should
be easy:



function swap(int a , int b);
{
int temp;
temp = a;
a = b;
b = temp;
}



the only complication being the need to use a third variable
temp to hold the value of a while
the value of b overwrites it. However, if you try
this function you will find that it doesn't work. You can use it
- swap(a,b); - until you are blue in the face,
but it just will not change the values stored in

a and b back in the calling
program. The reason is that all parameters in C are
passed by value. That is, when you use
swap(a,b) function the values in
a and b are passed into the
function swap via the parameters and any changes
that are made to the parameters do not alter a

and b back in the main program. The function
swap does swap over the values in
a and b within the function, but
doesn't do so in the main program.



The solution to this very common problem is to pass not the
values stored in the variables, but the addresses of
the variables
. The function can then use pointers to get at
the values in the variables in the main program and modify them.
That is, the function should be:




function swap(int *a , int *b);
{
int temp;
temp = *a;
*a = *b;
*b = temp;
}




Notice that now the two parameters a and
b are pointers and the assignments that effect
the swap have to use the de-reference operator to make sure
that it is the values of the variables pointed at
that are swapped. You should have no difficulty with:



temp = *a;



this just stores the value pointed at by a
into temp. However,



*a = *b;



is a little more unusual in that it stores that value pointed
at by b in place of the value pointed at by

a. There is one final complication. When you use
swap you have to remember to pass the addresses
of the variables that you want to swap. That is not:



swap(a,b)



but



swap(&a,&b)



The rule is that whenever you want to pass a variable so that
the function can modify its contents you have to pass it as an
address. Equally the function has to be ready to accept an
address and work with it. You can't take any old function and
suddenly decide to pass it the address of a variable instead of
its value. If you pass an address to a function that isn't
expecting it the result is usually disaster and the same is true
if you fail to pass an address to a function that is expecting
one.



For example, calling swap as swap(a,b) instead
of swap(&a,&b) will result in two arbitrary areas
of memory being swapped over, usually with the result that the
entire system, not just your program, crashes.



The need to pass an address to a function also explains the
difference between the two I/O functions that we have been using
since the beginning of this course. printf

doesn't change the values of its parameters so it is called as
printf("%d",a) but scanf does,
because it is an input function, and so it is called as
scanf("%d",&a).






Pointers And Arrays



In C there is a very close connection between pointers and
arrays. In fact they are more or less one and the same thing!
When you declare an array as:

int a[10];



you are in fact declaring a pointer a to the
first element in the array. That is, a is exactly
the same as &a[0]. The only difference between
a and a pointer variable is that the array
name is a constant pointer - you cannot change the location it
points at. When you write an expression such as

a[i] this is converted into a pointer expression
that gives the value of the appropriate element. To be more
precise, a[i] is exactly equivalent to
*(a+i) i.e. the value pointed at by a +
i
. In the same way *(a+ 1) is the same
as a[1] and so on.



Being able to add one to a pointer to get the next element of
an array is a nice idea, but it does raise the question of what
it means to add 'one' to a pointer. For example, in most
implementations an int takes two memory locations
and a float takes four. So if you declare an
int array and add one to a pointer to it, then in
fact the pointer will move on by two memory locations. However,
if you declare a float array and add one to a
pointer to it then the pointer has to move on by four memory
locations. In other words, adding one to a pointer moves it on by
an amount of storage depending on the type it is a pointer
to.



This is, of course, precisely why you have to declare the type
that the pointer is to point at! Only by knowing that

a is a pointer to int and
b is a pointer to float can the
compiler figure out that



a + 1



means move the pointer on by two memory locations i.e. add 2,
and



b + 1



means move the pointer on by four memory locations i.e. add 4.
In practice you don't have to worry about how much storage a
pointer's base type takes up. All you do need to remember is that
pointer arithmetic works in units of the data type that the
pointer points at. Notice that you can even use
++ and -- with a pointer, but not
with an array name because this is a constant pointer and cannot
be changed. So to summarise:




  1. An array's name is a constant pointer to the
    first element in the array that is a==&a[0] and

    *a==a[0].


  2. Array indexing is equivalent to pointer arithmetic - that is
    a+i=&a[i] and *(a+i)==a[i].



It is up to you whether you want to think about an array as an
array or an area of storage associated with a constant
pointer
. The view of it as an array is the more
sophisticated and the further away from the underlying way that
the machine works. The view as a pointer and pointer arithmetic
is more primitive and closer to the hardware. In most cases the
distinction is irrelevant and purely a matter of taste.


One final point connected with both arrays and
functions is that when you pass an entire
array to a function then by
default you pass a pointer. This allows you to write functions
that process entire arrays without having to pass every single
value stored in the array - just a pointer to the first element.
However, it also temps you to write some very strange code unless
you keep a clear head. Try the following - write a function that
will fill an array with random values
randdat(a,n) where a is the array
and n is its size. Your first attempt might be
something like:




void randdat(int *pa , int n)
{
for (pa = 0 ; pa < n ; pa++ ) *pa = rand()%n + 1;
}



Well I hope your first attempt wouldn't be like this because
it is wrong on a number of counts! The problem is that the idea
of a pointer and the idea of an index have been confused. The
pointer pa is supposed to point to the first
element of the array, but the for loop sets it to
zero and then increments it though a series of memory locations
nowhere near the array. A lesser error is to suppose that
n-1 is the correct final value of the array
pointer! As before, you will be lucky if this program doesn't
crash the system, let alone itself! The correct way of doing the
job is to use a for loop to step from

0 to n-1, but to use pointer
arithmetic to access the correct array element:




int randdat(int *pa , int n)
{
int i;
for ( i=0 ; i< n ; ++i)
{
*pa = rand()%n + 1;
++pa;
}
}



Notice the way that the for loop looks just
like the standard way of stepping through an array. If you want
to make it look even more like indexing an array using a

for loop you could write:



for(i=0 ; i<n ; ++i)
*(pa+i)=rand()%n+1;



or even:



for(i=0 ; i<n ; ++i) pa[i]=rand()%n+1;



In other words, as long as you define pa as a
pointer you can use array indexing notation with it and it looks
as if you have actually passed an array. You can even declare a
pointer variable using the notation:



int pa[];



that is, as an array with no size information. In this way the
illusion of passing an array to a function is complete.





Read more!

Strings







Strings





Objectives



This section brings together the use of two of C's
fundamental data types, ponters and arrays, in the use of
handling strings.



Having read this section you should be able to:




  1. handle any string constant by storing it in an array.






Stringing Along



Now that we have mastered pointers and the relationship between
arrays and pointers we can take a second look at strings.
A string is just a character array with the
convention that the end of the valid data is marked by a

null '\0'. Now you should be able to see why you can read
in a character string using scanf("%s", name)
rather than scanf("%s",&name) -
name is already a pointer variable. Manipulating
strings is very much a matter of pointers and special string
functions. For example, the strlen(str) function
returns the number of characters in the string
str. It does this simply by counting the number
of characters up to the first null in the character array - so it
is important that you are using a valid null-terminated string.
Indeed this is important with all of the C string
functions.


You might not think that you need a function to copy strings,
but simple assignment between string variables doesn't work. For
example:




char a[l0],b[10];
b = a;



does not appear to make a copy of the characters in
a, but this is an illusion. What actually happens
is that the pointer b is set to point to the same
set of characters that a points to, i.e. a second
copy of the string isn't created.



To do this you need strcopy(a,b) which really
does make a copy of every character in a in the
array b up to the first null character. In a
similar fashion strcat(a,b) adds the characters
in b to the end of the string stored in

a. Finally there is the all-important
strcmp(a,b) which compares the two strings
character by character and returns true - that is 0 - if the
results are equal.



Again notice that you can't compare strings using
a==b because this just tests to see if the two
pointers a and b are pointing to
the same memory location. Of course if they are then the two
strings are the same, but it is still possible for two strings to
be the same even if they are stored at different locations.



You can see that you need to understand pointers to avoid
making simple mistakes using strings. One last problem is how to
initialise a character array to a string. You can't use:



a = "hello";



because a is a pointer and
"hello" is a string constant. However, you can
use:



strcopy(a,"hello")



because a string constant is passed in exactly the same way as
a string variable, i.e. as a pointer. If you are worried where
the string constant is stored, the answer is in a special area of
memory along with all of the constants that the program uses. The
main disadvantage of this method is that many compilers use an
optimisation trick that results in only a single version of
identical constants being stored. For example:



strcopy(b,"hello");



usually ends up with b pointing to the same
string as a. In other words, this method isn't
particularly safe!



A much better method is to use array initialisation. You can
specify constants to be used to initialise any variable when it
is declared. For example:



int a=10;



declares a to be an integer and initialises it
to 10. You can initialise an array using a
similar notation. For example:



int a[5] = {1,2,3,4,5};



declares an integer array and initialises it so that
a[0]= 1, a[1] = 2 and so on. A
character array can be initialised in the same way. For
example:



char a[5]={'h','e','l','l','o'};



but a much better way is to write:



char a[6]="hello";



which also automatically stores a null character at the end of
the string - hence a[6] and not
a[5]. If you really want to be lazy you can
use:



char a[] = "hello";



and let the compiler work out how many array elements are
needed. Some compilers cannot cope with the idea of initialising
a variable that doesn't exist for the entire life of the program.
For those compilers to make initialisation work you need to add
the keyword static to the front of the string
declaration, therefore:



static char a[] = "hello";






As easy as... B or C?



A few words of warning. If you are familiar with BASIC
then you will have to treat C strings, and even C
arrays, with some caution. They are not as easy or as obvious to
use and writing a program that manipulates text is harder in C than in BASIC. If you try to use
C strings
as if it were BASIC strings you are sure to create some
very weird and wonderful bugs!




A Sort Of Bubble Program



This sections program implements a simple bubble sort - which is
notorious for being one of the worst sorting methods known to
programmer-kind, but it does have the advantage of being easy and
instructive. Some of the routines have already been described in
the main text and a range of different methods of passing data in
functions have also been used.

The main routine is sort which repeats the

scan function on the array until the variable
done is set to 0. The scan
function simply scans down the array comparing elements that are
next door to each other. If they are in the wrong order then
function swap is called to swap them over.



Study this program carefully with particular attention to the
way arrays, array elements and variables are
passed. It is worth saying that in some cases there are better
ways of achieving the same results. In particular, it would have
been easier not to use the variable done, but to
have returned the state as the result of the scan

function.




#include <stdio.h>


void randdat(int a[] , int n);
void sort(int a[] , int n);
void scan(int a[] , int n , int *done);
void swap(int *a ,int *b);

main()
{
int i;
int a[20];

randdat(a , 20);
sort(a , 20);

for(i=0;i<20;++i) printf("%d\n" ,a[i]);
}

void randdat(int a[1] , int n)
{
int i;
for (i=0 ; i<n ; ++i)
a[i] = rand()%n+1;
}

void sort(int a[1] , int n)
{
int done;
done = 1;
while(done == 1) scan(a , n , &done);
}

void scan(int a[1] , int n , int *done)
{
int i;
*done=0;
for(i=0 ; i<n-1 ; ++i)
{
if(a[i]<a[i+1])
{
swap(&a[i],&a[i+1]);
*done=1;
}
}
}

void swap(int *a ,int *b)
{
int temp;
temp = *a;
*a = *b;
*b = temp;
}




[program]


















Read more!

Structures








Structures





Objectives



This section contains some very advanced but important
features of the C programming language.



Having read this section you should be able to:




  1. program using a structure rather than several arrays.


  2. how pointer can be used in combination with structures to
    form linked list.






Structures



The array is an example of a data structure. It takes simple data
types like int, char or

double and organises them into a linear array of
elements. The array serves most but not all of the needs of the
typical C program. The restriction is that an array is
composed of elements all of the same type. At first this seems
perfectly reasonable. After all why would you want an array to be
composed of twenty chars and two
ints? Well this sort of mixture of data types
working together is one of the most familiar of data structures.
Consider for a moment a record card which records name,
age and salary. The name would have to be stored as
a string, i.e. an array of chars terminated with an ASCII
null character, and the age and salary could be

ints.

At the moment the only way we can work with this collection of
data is as separate variables. This isn't as convenient as a
single data structure using a single name and so the
C language provides struct. At first it is
easier to think of this as a record - although it's a little more
versatile than this suggests.






Defining A New Type



Declaring a struct is a two-stage process. The
first stage defines a new data type that has the required
structure which can then be used to declare as many variables
with the same structure as required. This two-stage process is
often confusing at first - especially as it results in the need
to think up multiple names with the same general meaning - but it
really is quite simple. For example, suppose we need to store a
name, age and salary as a single structure. You would first
define the new data type using:



struct emprec
{
char name[25];
int age;
int pay;
};



and then you would declare a new variable:

struct emprec employee



Notice that the new variable is called
employee and it is of type emprec
which has been defined earlier. You see what we mean about
duplicating names - emprec is the name of the
general employee record structure and

employee is a particular example of this general
type. It might help to compare the situation with that of a
general int type and a particular
int variable such as count -
emprec is a type like int and

employee is a variable like
count. You can see that in general you can define
a structure using:




struct name
{
list of component variables
};



and you can have as long a list of component variables as you
need. Once defined you can declare as many examples of the new
type as you like using:



struct name list of variables;



For example:



struct emprec employee, oldemploy,
newemploy;



and so on. If you want to you can also declare a structure
variable within the type definition by writing its name before
the final semi-colon. For example:




struct emprec
{
char name[25];
int age;
int pay;
} employee;




defines the structure and declares a structure variable called
employee. The only trouble with this form is that
not many C programmers use it and many will even think
that it is an error! So how do we use a
struct?



When you first start working with arrays it seems obvious that
you access the individual elements of the array using an index as
in a[i] for the ith element of the array, but how
to get at the individual components of a structure? The answer is
that you have to use qualified names. You first give the name of
the structure variable and then the name of the component
separated by a dot. For example, given:



struct emprec employee



then:



employee.age



is an int and:



employee.name



is a char array. Once you have used a
qualified name to get down to the level of a component then it
behaves like a normal variable of the type. For example:



employee.age=32;



is a valid assignment to an int and:



employee.name[2] = 'X';



is a valid assignment to an element of the
char array. Notice that the qualified name uses
the structure variable name and not the structure type name. You
can also define a structure that includes another structure as a
component and of course that structure can contain another
structure and so on. In this case you simply use the name of each
structure in turn, separated by dots, until you reach a final
component that isn't a structure. For example, if you declare a

struct firm which includes a component
employee which is an emprec
then:



firm.employee.age



is an int. You may be feeling a little
disappointed at the way in which structures are used. When you
first meet arrays it is obvious how useful they are because the
array index is an integer which can be used within a loop to
process vast amounts of data in a few lines of code. When you
first meet the struct it just doesn't have the
same obvious advantages. Because you have to write out a full
qualified name to get at each of the components of the

struct you can't automate the processing in the
same way. However this is reasonable enough when you remember
that each component of a struct can be a
different data type! The point is that the value of a
struct is different to that of an array. A
struct can be used to wrap up a group of
variables which form a coherent entity.



For example, C has no facilities for manipulating
complex numbers but this is easy enough to put right using a
struct and a few functions. A complex number is
composed of two parts - a real and imaginary part - which can be
implemented as single or double precision values. This suggests
defining a new struct type:




struct comp
{
float real;
float imag;
};



After this you can declare new complex variables using
something like:



struct comp a,b;



The new complex variables cannot be used as if they were
simple variables - because they are not. Most versions, of the
C language do allow you to assign structures so you could
write:



a=b;



as shorthand for




a.real=b.real;
a.imag=b.imag;



Being able to assign structures is even more useful when they
are bigger. However you can't expect C to sort out what
you mean by c = a + b - for this you have to
write out the rule for addition as:




c.real=a.real+b.real;
c.imag=a.imag+b.imag;







Structures and Functions



Of course a sensible alternative to writing out the addition each
time is to define a function to do the same job - but this raises
the question of passing structures as parameters. Fortunately
this isn't a big problem. Most C compilers, will allow you
to pass entire structures as parameters and return entire
structures. As with all C parameters structures are passed
by value and so if you want to allow a function to alter a
parameter you have to remember to pass a pointer to
a struct. Given that you can pass and return
structs the function is fairly easy:



struct comp add(struct comp a , struct comp b)
{
struct comp c;
c.real=a.real+b.real;
c.imag=a.imag+ b.imag;
return c;
}



After you have defined the add function you can write a
complex addition as:



x=add(y,z)



which isn't too far from the x=y+z that you
would really like to use. Finally notice that passing a
struct by value might use up rather a lot of
memory as a complete copy of the structure is made for the
function.






Pointers to Structures



You can define a pointer to a structure in the same way as any
pointer to any type. For example:

struct emprec *ptr



defines a pointer to an emprec. You can use a
pointer to a struct in more or less the same way
as any pointer but the use of qualified names makes it look
slightly different For example:



(*ptr).age



is the age component of the emprec structure
that ptr points at - i.e. an int.
You need the brackets because '.' has a higher priority than '*'.
The use of a pointer to a struct is so common,
and the pointer notation so ugly, that there is an equivalent and
more elegant way of writing the same thing. You can use:



prt->age



to mean the same thing as (*ptr).age. The
notation gives a clearer idea of what is going on -
prt points (i.e. ->) to the
structure and .age picks out which component of
the structure we want. Interestingly until C++ became
popular the -> notation was relatively rare
and given that many C text books hardly mentioned it this
confused many experienced C programmers!



There are many reasons for using a pointer to a
struct but one is to make two way communication
possible within functions. For example, an alternative way of
writing the complex number addition function is:




void comp add(struct comp *a , struct comp *b , struct comp *c)
{
c->real=a->real+b->real;
c->imag=a->imag+b->imag;
}



In this case c is now a pointer to a
comp struct and the function would be used
as:



add(&x,&y,&z);



Notice that in this case the address of each of the structures
is passed rather than a complete copy of the structure - hence
the saving in space. Also notice that the function can now change
the values of x, y and

z if it wants to. It's up to you to decide if
this is a good thing or not!






Malloc



Now we come to a topic that is perhaps potentially the most
confusing. So far we have allowed the C compiler to work
out how to allocate storage. For example when you declare a
variable:

int a;



the compiler sorts out how to set aside some memory to store
the integer. More impressive is the way that



int a[50]



sets aside enough storage for 50 ints and sets
the name a to point to the first element. Clever
though this may be it is just static storage. That is the storage
is allocated by the compiler before the program is run - but what
can you do if you need or want to create new variables as your
program is running? The answer is to use pointers
and the malloc function. The statement:



ptr=malloc(size);



reserves size bytes of storage and sets the pointer
ptr to point to the start of it. This sounds
excessively primitive - who wants a few bytes of storage and a
pointer to it? You can make malloc look a little
more appealing with a few cosmetic changes. The first is that you
can use the sizeof function to allocate storage
in multiples of a given type. For example:



sizeof(int)



returns a number that specifies the number of bytes needed to
store an int. Using sizeof you
can allocate storage using malloc as:



ptr= malloc(sizeof(int)*N)



where N is the number of ints

you want to create. The only problem is what does
ptr point at? The compiler needs to know what the
pointer points at so that it can do pointer arithmetic correctly.
In other words, the compiler can only interpret
ptr++ or ptr=ptr+1 as an
instruction to move on to the next int if it
knows that the ptr is a pointer to an

int. This works as long as you define the
ptr to be a pointer to the type of variable that
you want to work with. Unfortunately this raises the question of
how malloc knows what the type of the
pointer variable is - unfortunately it doesn't.



To solve this problem you can use a TYPE cast.
This C play on words is a mechanism to force a value to a
specific type. All you have to do is write the TYPE

specifier in brackets before the value. So:



ptr = (*int) malloc(sizeof(int)*N)



forces the value returned by malloc to be a
pointer to int. Now you can see how a simple idea
ends up looking complicated. OK, so now we can acquire some
memory while the program is running, but how can we use it? There
are some simple ways of using it and some very subtle mistakes
that you can make in trying to use it! For example, suppose
during a program you suddenly decide that you need an
int array with 50 elements. You didn't know this
before the program started, perhaps because the information has
just been typed in by the user. The easiest solution is to
use:



int *ptr;



and then later on:



ptr = (*int) malloc(sizeof(int)*N)



where N is the number of elements that you
need. After this definition you can use ptr as if
it was a conventional array. For example:



ptr[i]



is the ith element of the array. The trap waiting for you to
make a mistake is when you need a few more elements of the array.
You can't simply use malloc again to get the
extra elements because the block of memory that the next
malloc allocates isn't necessarily next to the
last lot. In other words, it might not simply tag on to the end
of the first array and any assumption that it does might end in
the program simply overwriting areas of memory that it doesn't
own.



Another fun error that you are not protected against is losing
an area of memory. If you use malloc to reserve
memory it is vital that you don't lose the pointer to it. If you
do then that particular chunk of memory isn't available for your
program to use until it is restarted.






Structures and Linked Lists



You may be wondering why malloc has been
introduced right after the structure. The answer is that the
dynamic allocation of memory and the struct go
together a bit like the array and the for loop.
The best way to explain how this all fits together is via a
simple example. You can use malloc to create as
many variables as you want as the program runs, but how do you
keep track of them? For every new variable you create you also
need an extra pointer to keep track of it. The solution to this
otherwise tricky problem is to define a struct

which has a pointer as one of its components. For example:


struct list
{
int data;
struct list *ptr;
};



This defines a structure which contains a single
int and - something that looks almost paradoxical
- a pointer to the structure that is being defined. All you
really need to know is that this is reasonable and it works. Now
if you use malloc to create a new
struct you also automatically get a new pointer
to the struct. The final part of the solution is
how to make use of the pointers. If you start off with a single
'starter' pointer to the struct you can create
the first new struct using malloc

as:




struct list *start;
start = (*struct list) malloc(sizeof(struct list))



After this start points to the first and only example of the
struct. You can store data in the struct using
statements like:



start->data=value;



The next step is to create a second example of the
struct:



start = (*struct list)
malloc(sizeof(list));



This does indeed give us a new struct but we
have now lost the original because the pointer to it has been
overwritten by the pointer to the new struct. To
avoid losing the original the simplest solution is to use:





struct list *start,newitem;
newitem = (*struct list) malloc(sizeof(struct list));
start->prt=start;
start=newitem;



This stores the location of the new struct in
newitem. Then it stores the pointer to the existing
struct into the newitem's pointer and sets
the start of the list to be the newitem. Finally
the start of the list is set to point at the new

struct. This procedure is repeated each time a
new structure is created with the result that a linked list of
structures is created. The pointer start always points to the
first struct in the list and the
prt component of this struct
points to the next and so on. You should be able to see how to
write a program that examines or prints the data in each of the
structures. For example:




thisptr=start;
while (1==1)
{
printf("%d",thisprt-> data);
thisprt=thisprt->prt;
}




This first sets thisptr to the start of the
list, prints the data in the first element and then gets the
pointer to the next struct in the list and so on.
How does the program know it has reached the end of the list? At
the moment it just keeps going into the deep and uncharted
regions of your machine's memory! To stop it we have to mark the
end of the list using a null pointer. Usually a pointer value of
0 is special in that it never occurs in a pointer pointing at a
valid area of memory. You can use 0 to initialise a pointer so
that you know it isn't pointing at anything real. So all we have
to do is set the last pointer in the list to 0 and then test for
it That is:




thisptr=start;
while (thisptr!=0)
{
printf("%d",thisprt->data);
thisprt=thisprt-> prt;
}




To be completely correct you should TYPE cast 0 to be a
pointer to the struct in question. That is:



while (thisptr!=(struct list*)0)



By generally mucking about with pointers

stored in the list you can rearrange it, access it, sort it,
delete items and do anything you want to. Notice that the
structures in the list can be as complicated as you like and,
subject to there being enough memory, you can create as many
structures as you like.



You can use the same sort of technique to create even more
complicated list structures. For example you can introduce
another pointer into each structure and a pointer to the end of
the list so that you can work your way along it in the other
direction - a doubly linked list. You can create
stacks, queues, trees and so
on. The rest of the story is a matter of either inventing these
data structures for yourself or looking them up in a suitable
book.






Structures and C++



The reason why structures are even more important for today's
budding C programmer is that they turn into classes
in C++. A class is a structure where you can define
components that are functions. In this case the same distinction
between a data TYPE and an example of the TYPE,
i.e. a variable, is maintained only now the instances of the
class include functions as well as data. The same
qualified naming system applies to the class and the use
of pointers and the -> operator. As this is
the basis of C++'s object-oriented features it is
important to understand.





Header Files



The final mystery of C that needs to be discussed is the
header file. This started off as a simple idea, a
convenience to make programming easier. If you have a standard
set of instructions that you want to insert in a lot of programs
that you are writing then you can do it using the
#include statement.

The # symbol at the start indicates that this
isn't a C statement but one for the C pre-processor
which looks at the text file before the compiler gets it. The
#include tells the pre-processor to read in a
text file and treat it as if it was part of the program's text.
For example:



#include "copy.txt"



could be used to include a copyright notice stored in the file
copy.txt. However the most common use of the
#include is to define constants and
macros. The C pre-processor is almost a
language in its own right For example, if you define the
identifier NULL as:



#define NULL 0



then whenever you use NULL in your program the
pre-processor substitutes 0. In most cases you want these
definitions to be included in all your programs and so the
obvious thing to do is to create a separate file that you can
#include.



This idea of using standard include files has spiralled out of
all proportions. Now such include files are called header
files
and they are distinguished by ending in the
extension .h. A header file is generally used to
define all of the functions, variables and constants contained in
any function library that you might want to use. The header file

stdio.h should be used if you want to use the two
standard I/O functions printf and
scanf. The standard libraries have been covered
in a previous section.



This sort of use of header files is simple enough but over
time more and more standard elements of the C environment
have been moved into header files. The result is that header
files become increasingly mysterious to the beginner. Perhaps
they reach their ultimate in complexity as part of the Windows
development environment So many constants and macros are defined
in the Windows header files that they amount to hundreds of
lines! As another example of how you could use a header file
consider the complex structure defined earlier. At the moment it
looks messy to declare a new complex variable as:



struct comp a,b;



If you want to make the complex TYPE look like other data
types all you need is a single #define



#define COMPLEX struct comp



After this you can write:



COMPLEX a,b;



and the pre-processor will automatically replace
COMPLEX by struct comp for you
when you compile the program. Put this #define

and any others needed to make the complex number type work and
you have the makings of a complex.h header file of your very own.











Read more!