home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 June
/
SIMTEL_0692.cdr
/
msdos
/
c
/
teach_c.arc
/
LESSON11
< prev
next >
Wrap
Text File
|
1986-04-04
|
23KB
|
517 lines
.NT
A NOTE ABOUT THE LESSONS in C
.b4-24R5C4
These were written while the author was ~Ilearning~N the language and since
.R6C4
they are ~Ifree~N ( to copy and/or distribute ) there is a money-back
.R7C4
guarantee on the accuracy of each and every statement in the lessons (!)
.R9C4
The ~Idisplay~N program was written ( in C ) in order to provide a vehicle
.R10C4
for displaying the lessons.
.R12C5
.B
P.J.Ponzo
.B
Dept. of Applied Math
.B
Univ. of Waterloo
.B
Ontario N2L 3G1
.K16,32
PonzoTUTOR
.WNT
a little STRUCTURE
.R5C1
If we wish to keep a record of, say, friends ... including name,
address, birthdate, etc. we might declare:
~b~Ichar name[20], address[40], birthdate[15];~N
where ~b~Iname~N, for example, is an array of 20 characters which is meant to
hold the name of one such friend, and ~b~Iaddress~N holds 40 ~b~Ichar~Ns, etc.
If we want 100 such records, we could use:
~b~Ichar name[100][20], address[100][40], birthdate[100][15];~N
where name[0], name[1],name[2], etc. are each arrays of 20 characters, etc.
We would print our list via a statement like:
~b~Ifor (i=0; i<100; i++) printf("\n%s %s %s",name[i],address[i],birthdate[i]);~N
.WN
Since the ~b~Ibirthdate~N has the form "Nov 6, 1934" (for example) we would
need to extract the last number ( 1934), and perform a subtraction, in
order to determine his (her?) age ... and would need all such numbers in
order to deduce the average age of our friends. So we might declare
~b~Ibirthdate~N to be a trio of objects: an array of characters (to hold
the birth-month, like "Nov"), an integer (to hold the birth-day) and a
second integer (to hold the birth-year) ... that way we could perform some
arithmetic on the integer parts of the ~b~Ibirthdate~N.
~b~Ichar name[100][~V20~N~b~I], address[100][~V40~N~b~I]; ~N
~b~Ichar birth_month[100][~V4~N~b~I]; ~N
~b~Iint birth_day[100], birth_year[100]; ~N
The above would do it. Sufficiently many arrays (of characters and
integers) for 100 friends, each with names of ~V 19 ~N characters (or less)
and addresses of ~V 39 ~N characters (or less) and ~V 3 ~N characters for
the birth_month (we'll need the terminating '\0' in each char array!)
... and 100 birth_days and birth_years (remember that birth_day[0] to
birth_day[99] is 100 birth-days!)
.w
It would be nice to have a DATA TYPE which held ~Ione~N such record, with
name, address, etc. and mixed ~b~Ichar~Ns and ~b~Iint~Ns !!
.WK8,60
amen!
.WN
.R2C1B
... let's welcome the STRUCTURE ...
.R6C1
We invent a ~Istructure~N called ~b~Idate~N which includes a ~V3~N character
~b~Imonth~N ( hence month[~V4~N] ) and two ~b~Iint~Ns (day and year).
We are accustomed to saying ~b~Iint x;~N and ~b~Ichar y;~N, meaning that
~b~Ix~N is of DATA TYPE ~b~Iint~N and ~b~Iy~N is of DATA TYPE ~b~Ichar~N.
SO, we will want to say ~b~Idate birth;~N meaning that ~b~Ibirth~N is
of DATA TYPE ~b~Idate~N meaning a collection of objects:
a ~b~Ichar month[4]~N, an ~b~Iint day~N and an ~b~Iint year~N.
But, if ~b~Idate~N is a ~b~Istruc~Nture (with the elements mentioned above)
then we will refer to it as ~b~Istruct date~N, to inform the compiler that
~b~Idate~N is no ordinary guy but is, in fact, a ~b~Istruct~N ...
with all the rights and privileges thereto appertaining!
~ISO~N, we declare ~b~Ibirth~N to be such a structure (called ~b~Idate~N) via:
~b~Istruct date birth;~N see? ~Vstruct date~N go together!
.WK8,32
confusing!
.WN
So why wouldn't we just define a ~b~Istruct~Nure called ~b~Ibirth~N, which
has the 3 members: ~b~Imonth[4]~N and ~b~Iint day~N and ~b~Iint year~N ??
Because we will want to use this ~b~Istruc~N for ~Iother~N ~b~Idate~Ns, like:
~b~Istruct date death;~N
Too morbid? Then how about:
~b~Istruct date WhenWeMet;~N
Now, ~b~IWhenWeMet~N is of DATA TYPE ~b~Idate~N too, containing the same
3 elements of ~b~Ichar month[4]~N and ~b~Iint day~N and ~b~Iint year~N !!
(Note: some compilers may not recognize names like ~b~IWhenWeMet~N
but may restrict names to, say, ~I8~N characters or less).
.WK19,32
go on!
.WNT
inside a STRUCTure
.R5C1
Before we see how to define such a ~b~Istruct~Nure as ~b~Idate~N, we use it!
It will have ~I3~N members: ~b~Imonth~N, ~b~Iday~N, ~b~Iyear~N.
We declare:
~b~Istruct date birth[100], WhenWeMet[100];~N
so each of ~b~Ibirth[0]~N, ~b~Ibirth[1]~N, etc. and ~b~IWhenWeMet[0]~N, ~b~IWhenWeMet[1]~N, etc.
are ~b~Istruct~Ns of TYPE ~b~Idate~N.
We refer to ~b~Ibirth[i]~F.~N~b~Imonth~N, ~b~Ibirth[i]~F.~N~b~Iday~N and ~b~Ibirth[i]~F.~N~b~Iyear~N and to
~b~IWhenWeMet[i]~F.~N~b~Imonth~N, ~b~IWhenWeMet[i]~F.~N~b~Iday~N and ~b~IWhenWeMet[i]~F.~N~b~Iyear~N
for i=0, 1, 2, ... up to the number of friends we have ( and each
reflects the ~I3~N members ~b~Imonth~N, ~b~Iday~N and ~b~Iyear~N of the
~b~Idate~N structure ... OK?)
.K18,32
note the
.
.WN
To input all this information we might use:
.R4C1
~b~Iprintf("\n How many friends do you have "); scanf("%s",&number); ~N
~b~Ifor (i=0; i<number; i++) { ~N
~b~I printf("\n For friend %d",i); ~N
~b~I printf("\n Enter Month of Birth "); scanf("%s",&birth[i].month); ~N
~b~I printf("\n Enter Day of Birth "); scanf("%s",&birth[i].day); ~N
~b~I printf("\n Enter Year of Birth "); scanf("%s",&birth[i].year); ~N
~b~I} ~N
.s
.R4C1
~Vprintf("\n How many friends do you have "); scanf("%s",&number); ~N
.r
Here we ask for the number of friends, and store it in ~b~Inumber~N
(note the ~V&~Nnumber!)
.WR4C1
~b~Iprintf("\n How many friends do you have "); scanf("%s",&number); ~N
~Vfor (i=0; i<number; i++) { ~N
.r
Then, we go through each of ~b~Inumber~N of friends, asking questions:
.WR5C1
~b~Ifor (i=0; i<number; i++) { ~N
~V printf("\n For friend %d:",i+1); ~N
.r
We remind the user which friend we're working on by ~b~Iprintf~Ning ...
~r~I For friend 1:~N then ~r~I For friend 2:~N etc.
(We don't refer to "friend 0:" ... that would be insulting ...so we print
the numbers ~b~Ii+1~N rather than ~b~Ii~N).
.WR6C1
~b~I printf("\n For friend %d",i); ~N
~V printf("\n Enter Month of Birth "); scanf("%s",&birth[i].month); ~N
.r
We ask ~r~I Enter Month of Birth ~N and put the answer into ~V&birth[i].month~N
(for the ~Ii~Nth friend) and, as required by ~b~Iscanf()~N,
we use the ~I&ddress~N !
.WR7C1
~b~I printf("\n Enter Month of Birth "); scanf("%s",&birth[i].month); ~N
~V printf("\n Enter Day of Birth "); scanf("%s",&birth[i].day); ~N
~V printf("\n Enter Year of Birth "); scanf("%s",&birth[i].year); ~N
.r
... and so on, for the ~b~Ibirth.day~N and ~b~Ibirth.year~N
.WN
... unfortunately, this won't (quite) work ... did you see why?
.WR2C1
We'll repeat the program excerpt:
~b~Iprintf("\n How many friends do you have "); scanf("%~Fs~N~b~I",&number); ~N
~b~Ifor (i=0; i<number; i++) { ~N
~b~I printf("\n For friend %d",i); ~N
~b~I printf("\n Enter Month of Birth "); scanf("%s",&birth[i].month); ~N
~b~I printf("\n Enter Day of Birth "); scanf("%~Fs~N~b~I",&birth[i].day); ~N
~b~I printf("\n Enter Year of Birth "); scanf("%~Fs~N~b~I",&birth[i].year); ~N
~b~I} ~N
~b~Inumber~N should have a ~b~I%d~N (for an integer)
~b~Ibirth[i].day~N should have a ~b~I%d~N (for an integer)
~b~Ibirth[i].year~N should have a ~b~I%d~N (for an integer)
... but there's something else ...
.sWR6C1
~V printf("\n Enter Month of Birth "); scanf("%s",&birth[i].month); ~N
.r
~b~Iscanf()~N will put the ~I3~N characters typed at the keyboard, say "Nov",
into the memory reserved for ~b~Ibirth[i].month~N, but won't put in a ~I'\0'~N!
WE must put it in ( ... while we're praying that the user doesn't type
november, which is much too long to fit into the ~I4~N bytes we've reserved
for the month!)
.WN
We could initialize all the bytes in the ~b~Ibirth.month~N to ~I'\0'~N via:
~b~Ifor (i=0; i<100; i++) { /* for all 100 friends */~N
~b~I for (j=0; j<4; j++) { /* for each of 4 bytes */~N
~b~I birth[i].month+j='\0'; /* set byte to '\0' */~N
~b~I } /* end of inner "for" */~N
~b~I} /* end of outer "for" */~N
... then (provided the user doesn't type more than a ~I3~N character month!)
we've got all the ~I'\0' string terminators~N we'll need.
This little ritual is necessary because ~b~Iscanf()~N is meant as a
general-purpose input ... ~b~Iint~Ns and ~b~Ifloats~N and ~b~Ichar~Ns etc.
A special-purpose input ... just for ~Istrings~N of ~b~Ichar~Ns ... would
be smart enough to append the ~I'\0'~N (wouldn't it?)
The ~Istdio.h~N library of C-functions will contain such a function.
~b~Igets(&sam)~N will ~b~Iget~N a ~b~Is~Ntring and put it into the
address ~b~I&sam~N. Just like scanf() requires a ~r~Ipointer~N to the memory
location where the input is to be stored, so does gets().
.WN
We write:
1 ~b~Iprintf("\n How many friends do you have "); scanf("%d",&number); ~N
2 ~b~Ifor (i=0; i<number; i++) { ~N
3 ~b~I printf("\n For friend %d",i); ~N
4 ~b~I printf("\n Enter Month of Birth "); gets(&birth[i].month); ~N
5 ~b~I printf("\n Enter Day of Birth "); scanf("%d",&birth[i].day); ~N
6 ~b~I printf("\n Enter Year of Birth "); scanf("%d",&birth[i].year); ~N
7 ~b~I} ~N
and the ~b~Igets()~N in line 4 will collect each ~b~Ichar~Naracter typed,
and when a ~I\n~Newline is typed (the Return or Enter key) it will exchange
it for a ~I'\0'~N ... and put everything into the memory location indicated.
.K16,32
wunderbar!
.WNT
defining a STRUCTure
.R5C1
It's about time we ~Idefined~N our ~b~Istruct date~N :
~b~I struct date { ~N
~b~I char month[4]; ~N
~b~I int day; ~N
~b~I int year; ~N
~b~I }; ~N
Note the structure of a structure:
~b~I struct ~Vname~N~b~I ~F{~N~b~I ~N
~b~I --- all the --- ~N
~b~I --- members --- ~N
~b~I --- go here --- ~N
~b~I ~F}~N~V;~N~b~I ~N
We give it a ~Vname~N (like ~b~Idate~N) so we can declare other objects to
be of this DATA TYPE ( remember ~b~Ibirth~N and ~b~IWhenWeMet~N ? )
... and an opening and closing ~F~b~I{~N and ~F~b~I}~N
... and the various members (like ~b~Ichar month[4]~N, etc.)
... and a final ~V ; ~N
.WK6,60
final ; ??
.WN
~b~I struct date { ~N
~b~I char month[4]; ~N
~b~I int day; ~N
~b~I int year; ~N
~b~I }~F;~N~b~I ~N
This final ~F;~N is meaningful!
Because a ~b~Istruct date~N is to be used just like ~b~Iint~N or ~b~Ichar~N,
and because we usually say ~b~Iint x~F;~N or ~b~Ichar x~F;~N (with a final ~F;~N),
then we terminate a structure definition with a ~F;~N and expect to be able
to say ~b~Istruct date { ~N
~b~I --- etc --- ~N
~b~I} x~F;~N~b~I ~N (note the similarity with ~b~Iint x;~N etc.)
.w
SO, for our earlier example, we could say:
~b~I struct date { ~N
~b~I char month[4]; ~N
~b~I int day; ~N
~b~I int year; ~N
~b~I } birth[100],WhenWeMet[100]~F;~N~b~I ~N
... and we've defined our ~b~Istruct date~N AND declared ~b~Ibirth[]~N
and ~b~IWhenWeMet[]~N to be such structures ... all at once!
.K2,60
how nice!
.WN
Now let's return to the record of our "friends", which includes ~b~Iname~N
and ~b~Iaddress~N as well as some ~b~Idates~N.
For each "record" we will define ~Ianother~N structure ... let's call it
"record" (what else?)
~b~I struct record { ~N
~b~I char name[20]; ~N
~b~I char address[40]; ~N
~b~I struc date birth; ~N
~b~I struc date WhenWeMet; ~N
~b~I } friend[100]; ~N
Note that we've not only defined the ~b~Istruct~N called ~b~Irecord~N but we've
also declared 100 such structures, using the "final" ~b~Ifriend[100];~N
~b~Ifriend[0]~N and ~b~Ifriend[1]~N and ~b~Ifriend[2]~N etc. are ALL of type
~b~Irecord~N hence contain members ~b~Iname~N, ~b~Iaddress~N, ~b~Ibirth~N
and ~b~IWhenWeMet~N.
.w
The first two (~b~Iname~N and ~b~Iaddress~N) are ~b~Ichar~Nacter arrays
~IBUT~N the last two (~b~Ibirth~N and ~b~IWhenWeMet~N) are ... SURPRISE (!)
~Istructures~N of TYPE ~b~Idate~N !!!
.WN
We now have two structures defined:
~b~I struct date { ~N ~b~I struct record { ~N
~b~I char month[4]; ~N ~b~I char name[20]; ~N
~b~I int day; ~N ~b~I char address[40]; ~N
~b~I int year; ~N ~b~I struc date birth; ~N
~b~I }; ~N ~b~I struc date WhenWeMet; ~N
~b~I} friend[100]; ~N
Earlier we declared 200 structures of TYPE ~b~Idate~N (namely ~b~Ibirth[100]~N
and ~b~IWhenWeMet[100]~N). We also referred to the ~I3~N members of ~b~Ibirth[47]~N
(for example) as ~b~Ibirth[47]~F.~N~b~Imonth~N, ~b~Ibirth[47]~F.~N~b~Iday~N and ~b~Ibirth[47]~F.~N~b~Iyear~N.
Now we won't need to define these 200 structures (sorry about that!) since
~b~Istruct date birth~N and ~b~Istruct date WhenWeMet~N are embedded in the
~b~Irecord~N structure ... ~Istructures within structures~N!!
.K16,32
aw c'mon!!
.WNT
STRUCTures within STRUCTures ??
.R10C1
~V NOBODY SAID THIS WAS EASY! ~N
.b8-12
.K16,32
!@#$%^&*?!
.WN
~b~I struct date { ~N ~b~I struct record { ~N
~b~I char month[4]; ~N ~b~I char name[20]; ~N
~b~I int day; ~N ~b~I char address[40]; ~N
~b~I int year; ~N ~b~I struc date birth; ~N
~b~I }; ~N ~b~I struc date WhenWeMet; ~N
~b~I} friend[100]; ~N
To input (for example) the name of the friend[47], we'd say:
~b~Iprintf("\n Name please : "); gets(&friend[47].name);~N
... and ... to input his (her?) birth.month we'd say:
~b~Iprintf("\n Month of Birth : "); gets(&friend[47].birth.month);~N
Note the use of ~b~Igets()~N ( to automatically append the ~I'\0'~N ).
Note, too, the very logical way we refer to the member of a structure
within a structure ... ~b~Ifriend[47].birth.month~N
.K19,60
gorgeous!!
.WN
~b~Imain() { /* not-too-useful-program */ ~N
~b~I int number, i; ~N
~b~I struct date { ~N
~b~I char month[4]; ~N
~b~I int day; ~N
~b~I int year; ~N
~b~I }; ~N
~b~I struct record { ~N
~b~I char name[20]; ~N
~b~I char address[40]; ~N
~b~I struct date birth; ~N
~b~I } friend[100]; ~N
~b~I printf("\nHow many friends : "); scanf("%d",&number); ~N
.w ~N
~b~I for (i=0; i<number; i++) { ~N
~b~I printf("\n For friend %d",i+1); ~N
~b~I printf("\nName ? "); gets(&friend[i].name); ~N
~b~I printf("\nAddress ? "); gets(&friend[i].address); ~N
~b~I printf("\nMonth of birth ? "); gets(&friend[i].birth.month); ~N
~b~I printf("\nDay of birth ? "); scanf("%d",&friend[i].birth.day); ~N
~b~I printf("\nYear of birth ? "); scanf("%d",&friend[i].birth.year);~N
~b~I } ~N
.WN
~b~I printf("\nSUMMARY of your %d friends",number); ~N
~b~I for (i=0; i<number; i++) { ~N
~b~I printf("\nName:%s",friend[i].name); ~N
~b~I printf("\nAddress:%s",friend[i].address); ~N
~b~I printf("\nBorn on %s %d,%d", ~N
~b~I friend[i].birth.month,friend[i].birth.day,friend[i].birth.year);~N
~b~I } ~N
~b~I} ~N
giving a (typical) printout:
~r~IName:Peter Ponzo~N
~r~IAddress:49 Margaret S., Waterloo, Ont.~N
~r~IBorn on Nov 6,1934~N
.K16,32
a-a-a-ah!
.WNT
and POINTERS to STRUCTURES!
.R4C1
One sometimes feels frustrated in writing an elaborate function which does
the most wonderful things, only to get from such a function a single
~b~Iint~N or ~b~Ifloat~N or ~b~Ichar~N ( you can't ~b~Ireturn(a,b,c,d,e)~N
at the end of the function, but only ~b~Ireturn(a)~N !@#$% ).
BUT, the function can create an elaborate ~Istructure~N which houses all the
wonderful things, then ~b~Ireturn(a)~N where ~n~Ia~N is a ~r~Ipointer~N to
the structure!
In fact we've used such a function ... one which returns a ~r~Ipointer~N.
Before I tell you which function it is (from the ~Istdio.h~N library), let's
see how such a function should be declared.
Consider
~b~Ichar f();~N which declares ~b~If()~N to be a function which returns a ~b~Ichar~N
Then, to declare a function which returns a ~r~Ipointer~N
to a ~b~Ichar~N it would be sensible to use the format:
~b~Ichar *f();~N ... right?
.WK19,60
RIGHT!
.WN
~ISO~N ... if the function ~b~If()~N were to return a ~r~Ipointer~N to a
structure called ~b~Isam~N, we'd declare it with:
~b~Isam *f();~N ... right?
.WK16,32
RIGHT!
And what if ~b~Isam~N was ~b~Itypedef~Nined to be a structure, as in:
~b~Itypedef SomeStructure SAM;~N ( where we have agreed to use capitals )
Then the function ~b~If()~N would be declared:
~b~ISAM *f();~N ... right?
.WK16,32
.K16,32
RIGHT!!
.WN
~INOW~N ... remember when we used:
~b~IFILE *fopen();~N ???
In fact, when we ~b~Ifopen()~N a file on a disk, the operating system
returns a ~r~Ipointer~N to a ~Istructure~N ( called ~b~IFILE~N ) and this
structure contains all the wonderful things we need to know about the
file ... and that's why we also declare this ~r~Ipointer~N ~b~Ifp~N:
~b~IFILE *fp, *fopen();~N
and say (subsequent to the above declaration):
~b~I fp=fopen();~N so we assign to ~b~Ifp~N the ~r~Ipointer~N returned
by ~b~Ifopen()~N.
Then, when we want to get a character from this file, we need only pass to
~b~Igetc()~N this ~r~Ipointer~N ( as in ~b~Igetc(fp)~N ) and now the ~b~Igetc()~N
function will be able to extract all the wonderful things it needs ...
~Ifrom the structure~N!
.WK1,60
magnifico!
.WN
~INOW~N, if ~b~Isam *f();~N is the way we ~Ideclare f() to be a function~N
which returns a ~r~Ipointer~N to an object of TYPE ~b~Isam~N (which could
be an ~b~Iint~N or a ~b~Ifloat~N or a ~b~Istruct~N etc.), then how should
we ~Ideclare f to be a pointer~N to a function which returns an object
of TYPE ~b~Isam~N ????
.K19,60
I give up!
.W
~b~Isam (*f)();~N
says that ~b~If~N is now the ~r~Ipointer~N ... because we used ~b~I(*f)~N ...
and the thing it points to is ~b~I*f~N ( remember that ~b~Iint *x;~N declares
~b~Ix~N to be a ~r~Ipointer~N to an ~b~Iint~N, and the ~b~Iint~N is ~b~I*x~N )
~ISO~N, since ~b~I(*f)~N is to be a ~Ifunction~N, we say ~b~I(*f)()~N !
... and we've seen ~Ithis~N curious notation before too!
.WK19,60
amen!
.WN
Suppose that ~b~Iptr~N is a ~r~Ipointer~N to a ~Istruct~Nure
( declared using ~b~I*ptr~N so that ~b~I(*ptr)~N ~IIS~N the structure itself ).
Suppose the structure had a member called ~b~Iname~N.
Then we'd refer to this member as: ~b~I(*ptr).name~N
( as in ~b~I(*ptr).name="George";~N )
Another (simpler) notation ~Ifor the same thing~N is:
~b~Iptr -> name~N (use this only if ~b~Iptr~N is a ~r~Ipointer~N)
... and if the structure had a member ~b~Ibirth~N which was itself a structure
containing a member called ~b~Imonth~N, then we can use the notation:
~b~Iptr->birth.month~N ( which means ~b~I(*ptr).birth.month~N )
as in ~b~Iptr->birth.month="May";~N
~IAND~N, if ~b~Ibirth~N happened to be a ~r~Ipointer~N too, we'd use:
~b~Iptr->birth->month~N
.w
.WK3,60
MORAL???
.W
Use ~b~Isam.birth ~N if ~b~Isam~N is a structure.
Use ~b~Isam->birth~N if ~b~Isam~N is a ~r~Ipointer~N to a structure.
.WN
.T
That's all folks!
.K16,32
au revoir!
.q