Output of file : LESSON11 contained in archive : TEACH-C.ZIP
.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
[1mPonzoTUTOR
.WNT
a little STRUCTURE
.R5C1
If we wish to keep a record of, say, friends ... including name,
address, birthdate, etc. we might declare:

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:

where name, name,name, 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 birth_month[~V4~N~b~I]; ~N
~b~Iint birth_day, birth_year; ~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 to
birth_day 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~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~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

~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~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, WhenWeMet;~N

so each of ~b~Ibirth~N, ~b~Ibirth~N, etc. and ~b~IWhenWeMet~N, ~b~IWhenWeMet~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 [5m.[0m
.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 ~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 .r

Then, we go through each of ~b~Inumber~N of friends, asking questions:
.WR5C1
~b~Ifor (i=0; i ~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 ~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 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; ~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~N, etc.)
... and a final ~V ; ~N
.WK6,60
final ; ??
.WN
~b~I struct date { ~N
~b~I char month; ~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; ~N
~b~I int day; ~N
~b~I int year; ~N
~b~I } birth,WhenWeMet~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; ~N
~b~I struc date birth; ~N
~b~I struc date WhenWeMet; ~N
~b~I } friend; ~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;~N

~b~Ifriend~N and ~b~Ifriend~N and ~b~Ifriend~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; ~N ~b~I char name; ~N
~b~I int day; ~N ~b~I char address; ~N
~b~I int year; ~N ~b~I struc date birth; ~N
~b~I }; ~N ~b~I struc date WhenWeMet; ~N
~b~I} friend; ~N

Earlier we declared 200 structures of TYPE ~b~Idate~N (namely ~b~Ibirth~N
and ~b~IWhenWeMet~N). We also referred to the ~I3~N members of ~b~Ibirth~N
(for example) as ~b~Ibirth~F.~N~b~Imonth~N, ~b~Ibirth~F.~N~b~Iday~N and ~b~Ibirth~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; ~N ~b~I char name; ~N
~b~I int day; ~N ~b~I char address; ~N
~b~I int year; ~N ~b~I struc date birth; ~N
~b~I }; ~N ~b~I struc date WhenWeMet; ~N
~b~I} friend; ~N
To input (for example) the name of the friend, we'd say:

~b~Iprintf("\n Name please : "); gets(&friend.name);~N

... and ... to input his (her?) birth.month we'd say:

~b~Iprintf("\n Month of Birth : "); gets(&friend.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.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; ~N
~b~I int day; ~N
~b~I int year; ~N
~b~I }; ~N
~b~I struct record { ~N
~b~I char name; ~N
~b~I struct date birth; ~N
~b~I } friend; ~N
~b~I printf("\nHow many friends : "); scanf("%d",&number); ~N
.w ~N
~b~I for (i=0; i ~b~I printf("\n For friend %d",i+1); ~N
~b~I printf("\nName ? "); gets(&friend[i].name); ~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 ~b~I printf("\nName:%s",friend[i].name); ~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~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
[1mRIGHT!![0m
.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

### 3 Responses to “Category : C Source CodeArchive   : TEACH-C.ZIPFilename : LESSON11”

1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/