home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 June
/
SIMTEL_0692.cdr
/
msdos
/
c
/
teach_c.arc
/
LESSON9
< prev
next >
Wrap
Text File
|
1986-02-08
|
21KB
|
395 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
the big SWITCH
.R5C1
You may recall, from an earlier lesson, that we checked various cases by
using the ~Iif-else if~N construction:
1 ~b~I if ( such-and-such) { ~N
2 ~b~I ----do this----- ~N
3 ~b~I } ~N
4 ~b~I else if (this-or-that) { ~N
5 ~b~I ----do this----- ~N
6 ~b~I } ~N
7 ~b~I else if ( whatever ) { ~N
8 ~b~I ----do this----- ~N
9 ~b~I } ~N
10~b~I else { ~N
11~b~I ----do this----- ~N
12~b~I } ~N
13~b~I ----continuation of program ~N
.R20C1
Realize that ~Ionly one~N of the statements 2, 5, 8, 11 will be
executed, depending upon which of the conditions 1, 4, 7, 10 is satisfied
first. (If none of 1,4 or 7 are satisfied, then 10 ~IIS~N satisfied and
11 will be executed).
.w
.WR20C1
.R20C1
Even if the conditions 1 ~Iand~N 4 ~Iand~N 7 are all satisfied, only
statement(s) 2 will be executed, then the program will continue with
statement 13, etc.
.WR20C1
.R20C1
(There's a MORAL here. To speed up execution, put the ~Imost probable~N
condition first ... then the program won't have to do so much checking).
.WN
But there's another (more natural) way of checking a number of cases in C.
.R5C1
~b~I switch (x) { /* Begin the SWITCH on the integer x. */ ~N
~b~I case 1: /* If x is the integer 1, then */ ~N
~b~I do this; /* execute this statement ... */ ~N
~b~I and this; /* and this too. */ ~N
~b~I case 2: /* If x is the integer 2, then */ ~N
~b~I do this; /* execute this statement ... */ ~N
~b~I and this; /* and this too. */ ~N
~b~I case 3: /* If x is the integer 3, then */ ~N
~b~I do this; /* execute this statement ... */ ~N
~b~I and this; /* and this too. */ ~N
~b~I and this; /* and this too. */ ~N
~b~I and this; /* and this too. */ ~N
~b~I case 4: /* If x is the integer 4, then */ ~N
~b~I do this; /* execute this statement ... */ ~N
~b~I default: /* If x is none of the above, then */ ~N
~b~I do this; /* execute this statement ... */ ~N
~b~I } ~N
.w
.W
Notice the opening and closing brackets for the SWITCH!
.R5C1
~b~I switch (x) ~F{~N
.R21C1
~b~I ~F}~N
.WN
... and here's a variation ...
.R5C1
1 ~b~I switch (x) { /* Begin the SWITCH on the integer x. */ ~N
2 ~b~I case 1: /* If x is the integer 1, then */ ~N
3 ~b~I case 2: /* If x is the integer 2, then */ ~N
4 ~b~I case 3: /* If x is the integer 3, then */ ~N
5 ~b~I do this; /* execute this statement ... */ ~N
6 ~b~I and this; /* and this too. */ ~N
7 ~b~I and this; /* and this too. */ ~N
8 ~b~I and this; /* and this too. */ ~N
9 ~b~I case 4: /* If x is the integer 4, then */ ~N
10~b~I do this; /* execute this statement ... */ ~N
11~b~I default: /* If x is none of the above, then */ ~N
12~b~I do this; /* execute this statement ... */ ~N
13~b~I } ~N
If the ~b~Iint~Neger ~b~Ix~N is equal to ~I1 or 2 or 3~N then statements
5 to 8 will be executed! (...so the SWITCH will NOT STOP with the first
case that is satisfied, but will check ALL SUBSEQUENT CASES!)
.W
If you don't want that to happen, then you may terminate a case with
a ~Fbreak~N.
.K5,32
a WHO ?
.WN
1 ~b~I switch (x) { /* Begin the SWITCH on the integer x. */ ~N
2 ~b~I case 1: /* If x is the integer 1, then */ ~N
3 ~b~I case 2: /* If x is the integer 2, then */ ~N
4 ~b~I case 3: /* If x is the integer 3, then */ ~N
5 ~b~I do this; /* execute this statement ... */ ~N
6 ~b~I and this; /* and this too. */ ~N
7 ~b~I and this; /* and this too. */ ~N
8 ~b~I and this; /* and this too. */ ~N
9 ~b~I ~Fbreak;~N~b~I /* and now BREAK OUT OF THE SWITCH! */ ~N
10~b~I case 4: /* If x is the integer 4, then */ ~N
11~b~I do this; /* execute this statement ... */ ~N
12~b~I default: /* If x is none of the above, then */ ~N
13~b~I do this; /* execute this statement ... */ ~N
14~b~I } ~N
Now, if ~b~Ix~N is a ~I1 or 2 or 3~N, the statements 5 to 8 will be
executed and (because of the ~b~Ibreak;~N in line 9) we leave the SWITCH
and continue beyond line 14.
If, however ~b~Ix~N is a ~I4~N, then only the statement(s) for this case
are executed (line 11, in this example).
~IOnly if all cases fail will the default statement(s) be executed~N
(for example, if ~b~Ix~N is a ~I6~N then statement 13 is executed).
.WNT
more SWITCHing
.R4C1
You may switch on any type of variable (not just ~b~Iint~Negers).
For example you may have declared ~b~Ix~N to be a ~b~Ichar~N, so ...
1 ~b~I switch (x) { /* Begin the SWITCH on the char x. */ ~N
2 ~b~I case 'A': /* If x is the character 'A', then */ ~N
3 ~b~I case 'u': /* If x is the character 'u', then */ ~N
4 ~b~I case '#': /* If x is the character '#', then */ ~N
5 ~b~I do this; /* execute this statement ... */ ~N
6 ~b~I and this; /* and this too. */ ~N
7 ~b~I and this; /* and this too. */ ~N
8 ~b~I and this; /* and this too. */ ~N
9 ~b~I break; /* and now BREAK OUT OF THE SWITCH! */ ~N
Note that the ~Icase~N comparison must be consistent with the variable
type.
If ~b~Ix~N is an ~b~Iint~N then you may use ~b~Icase 7:~N
If ~b~Ix~N is a ~b~Ichar~N then you may use ~b~Icase '+':~N
If ~b~Ix~N is a ~b~Ifloat~N then you may use ~b~Icase -1.234:~N
.W
Think of the ~Icase~N comparisons as being equivalent to:
~b~I if (x==7)~N or ~b~Iif (x=='+')~N or ~b~I if (x==-1.234)~N etc.
... ~Iand you may leave out the ~Idefault~N if you wish!~N
.WNT
CALL BY VALUE and CALL BY REFERENCE
We mentioned in an earlier lesson that a ~Ifunction call~N, in which you
pass certain parameters ( like ~b~Iaverage(a,b)~N ), gives to the function
~Icopies~N of the parameters. The function may change these copies but
the "originals" won't be changed. This is CALL BY VALUE.
You may, however, WANT to have a function change the originals. In this
case you must tell the function where, in memory, the "originals" live.
To do this you may pass the ~Iaddresses~N of the parameters ( or ~r~Ipointers~N
to the parameters). This is CALL BY REFERENCE.
Knowing where the "original" parameters are, in memory, a function may
now modify them.
.WN
Suppose you want to ~b~Iexchange()~N the values of two ~b~Ifloat~Ning
point numbers, say ~b~Ix~N and ~b~Iy~N, by calling upon a function
~b~Iexchange()~N :
~b~Iexchange(&x,&y); /* call the function, give it addresses of x,y*/~N
The exchange funtion may look like:
~b~Iexchange(u,v) /* this function exchanges two "floats". */~N
~b~Ifloat *u, *v; /* declare u and v as pointers to "floats". */~N
~b~I{ /* the opening bracket for exchange(). */~N
~b~I float temp; /* declare a temporary float. */~N
~b~I temp=*u; /* make it equal to "what u points to". */~N
~b~I *u=*v; /* place the contents of v into u. */~N
~b~I *v=temp; /* place the "temp"orary float into v. */~N
~b~I return; /* return ... no need to return anything! */~N
~b~I} /* the floats have been exchanged !! */~N
.WN
You can try it out with:
~b~Imain() {
~b~I float x=1.23, y=4.56; /* declare and define two floats */~N
~b~I printf("\nx=%f, y=%f",x,y); /* printf their values. */~N
~b~I exchange(&x,&y); /* call the exchange program. */~N
~b~I printf("\nx=%f, y=%f",x,y); /* printf their values again! */~N
~b~I} /* that's the end of main(). */~N
~b~Iexchange(u,v) /* this function exchanges two "floats". */~N
~b~Ifloat *u, *v; /* declare u and v as pointers to "floats". */~N
~b~I{ /* the opening bracket for exchange(). */~N
~b~I float temp; /* declare a temporary float. */~N
~b~I temp=*u; /* make it equal to "what u points to". */~N
~b~I *u=*v; /* place the contents of v into u. */~N
~b~I *v=temp; /* place the "temp"orary float into v. */~N
~b~I return; /* return ... no need to return anything! */~N
~b~I} /* the floats have been exchanged !! */~N
Now exit the text editor, saving the above with the name ~Isam.c~N, then
compile using ~Icc sam~N, then link using ~Ilink sam~N, then execute via:
~Isam~N, and get:
~r~Ix=1.230000, y=4.560000~N
~r~Ix=4.560000, y=1.230000~N
.WK10,60
LOVELY!
.WN
Here's the ~b~Iexchange()~N function again:
~b~Iexchange(u,v) /* this function exchanges two "floats". */~N
~b~Ifloat *u, *v; /* declare u and v as pointers to "floats". */~N
~b~I{ /* the opening bracket for exchange(). */~N
~b~I float temp; /* declare a temporary float. */~N
~b~I temp=*u; /* make it equal to "what u points to". */~N
~b~I *u=*v; /* place the contents of v into u. */~N
~b~I *v=temp; /* place the "temp"orary float into v. */~N
~b~I return; /* return ... no need to return anything! */~N
~b~I} /* the floats have been exchanged !! */~N
Here's another variation:
~b~Iexchange(u,v) /* this function exchanges two "floats"~F?~N~b~I */~N
~b~Ifloat *u, *v; /* declare u and v as pointers to "floats". */~N
~b~I{ /* the opening bracket for exchange(). */~N
~b~I float *temp; /* declare a temporary ~Fpointer~N~b~I. */~N
~b~I temp=u; /* make it equal to the pointer "u". */~N
~b~I u=v; /* make "u" point to what "v" points to. */~N
~b~I v=temp; /* make "v" point to what "temp" points to. */~N
~b~I return; /* return ... no need to return anything! */~N
~b~I} /* the floats have been exchanged ~F?????~N~b~I */~N
Why won't the latter function work???
.WK6,32
I give up!
.WN
~b~Iexchange(u,v) ~V/* this function does NOT exchange floats! */~N
~b~Ifloat *u, *v; /* declare u and v as pointers to "floats". */~N
~b~I{ /* the opening bracket for exchange(). */~N
~b~I float *temp; /* declare a temporary pointer. */~N
~b~I temp=u; /* make it equal to the pointer "u". */~N
~b~I u=v; /* make "u" point to what "v" points to. */~N
~b~I v=temp; /* make "v" point to what "temp" points to. */~N
~b~I return; /* return. */~N
~b~I} ~V/* the floats have not been exchanged! */~N
In this variation, the pointers ~b~Iu~N and ~b~Iv~N are ~Icopies~N and,
although this function does change these copies of the pointers, their
contents do ~INOT~N change! ( so the ~b~Ifloat~Ns never do get exchanged! ).
.WK16,32
BEWARE!
.WNT
passing FUNCTIONS to FUNCTIONS
.R5C1
In an earlier lesson we computed the roots of some equation x=f(x),
with f(x)=2*sin(x).
1 ~b~I double x=1.0, y, e; /* double precision ! */ ~N
2 ~b~I do { /* start of the do-loop*/ ~N
3 ~b~I y=2.0*sin(x); /* calculate y */ ~N
4 ~b~I e=fabs(y-x); /* calculate error */ ~N
5 ~b~I x=y; /* change x to y */ ~N
6 ~b~I } while (e>.0000005); /* end condition */ ~N
7 ~b~I printf("x-2sin(x)=%f when x=%f",e,x); ~N
Now suppose we turn this piece of code into a function, ~b~Isolve()~N
which we call via:
~b~Iroot=solve(f,x,e);~N
where we pass to ~b~Isolve()~N the function ~b~If(x)~N, and some initial
guess of the root, namely ~b~Ix~N, and an error specification ~b~Ie~N.
We expect ~b~Isolve(f,x,e)~N to return a root (which, naturally, we call
root!).
.WN
We may write ~b~Isolve()~N like so:
1 ~b~Ifloat solve(fcn,x,error) /* returns a FLOAT! */ ~N
2 ~b~Ifloat (*fcn)(); /* !!!!!!!!!!!!!!!!!!!!!!!!! */ ~N
3 ~b~Ifloat x, error; /* x-value & error are floats. */ ~N
4 ~b~I{ ~N
5 ~b~I float y, e; /* declares 2 floats. */ ~N
6 ~b~I do { /* start of the do-loop. */ ~N
7 ~b~I y=(*fcn)(x); /* calculates y. */ ~N
8 ~b~I e=fabs(y-x); /* calculate absolute value of y-x.*/ ~N
9 ~b~I x=y; /* change x to y. */ ~N
10~b~I } while (e>error); /* check error if e is too large. */ ~N
11~b~I return(x); /* return x=root if e<=error. */ ~N
12~b~I} ~N
Line 2 has the curious declaration of ~b~Ifcn()~N as a ~r~Ifunction pointer~N.
The ~b~I (*fcn) ~N says it's a pointer, and the ~b~I()~N says it points
to a function and the ~b~Ifloat~N says this fcn returns a ~Ifloat~N!
Note too, in line 7, that whereas ~b~Ifcn~N is a pointer, ~b~I*fcn~N ~IIS~N
the function! ( The parentheses are necessary ).
.WN
.R1C1
~b~Imain() {
~b~I float f1(), f2(), f3(), solve(); /* declare functions used.*/~N
~b~I printf("\nA root of x=f1(x) is %f", /* printf the root ... */~N
~b~I solve(f1,1,.00005)); /* solve x=f1(x). */~N
~b~I printf("\nA root of x=f2(x) is %f", /* printf the root ... */~N
~b~I solve(f2,-1,.00005)); /* solve x=f2(x). */~N
~b~I printf("\nA root of x=f3(x) is %f", /* printf the root ... */~N
~b~I solve(f3,2,.00005)); /* solve x=f3(x). */~N
~b~I} ~N
~b~Ifloat f1(x) ~N
~b~Ifloat x; ~N
~b~I{ return(2.*sin(x)); } /* f1(x) = 2 sin(x) */~N
~b~Ifloat f2(x) ~N
~b~Ifloat x; ~N
~b~I{ return(2.-x/2.); } /* f2(x) = 2-x/2 */~N
~b~Ifloat f3(x) ~N
~b~Ifloat x; ~N
~b~I{ return(1.+1./x); } /* f3(x) = 1+1/x */~N
.w
.R2C1
~V float f1(), f2(), f3(), solve(); /* declare functions used.*/~N
.R19C1
Here we declare all the functions we use ( they all return a float).
.WR2C1
~b~I float f1(), f2(), f3(), solve(); /* declare functions used.*/~N
~V printf("\nA root of x=f1(x) is %f", /* printf the root ... */~N
~V solve(f1,1,.00005)); /* solve x=f1(x). */~N
.R19C1
Here we print (after a ~b~I\n~Newline) ~r~IA root of x=f1(x) is ~N
followed by the ~b~I%f~Nloat ~Ireturned~N by solve(f1,1,.00005)~N.
Note that we pass the ~Ipointer f1~N, a starting value ~I1~N
and an error specification of .00005
.WR3C1
~b~I printf("\nA root of x=f1(x) is %f", /* printf the root ... */~N
~b~I solve(f1,1,.00005)); /* solve x=f1(x). */~N
.R19C1
.w
... then we continue with two more functions f2(x) and f3(x), each time
specifying not only the ~r~Ipointer~N to the function but also a starting
value and error specification.
.WN
.R3C2
REMEMBER: To pass the function ~b~Isam(a,b,c)~N as an argument to another
function ~b~Igeorge(sam,x,y)~N, then include the declaration
~b~Ifloat (*sam)()~N ( make this declaration within ~b~Igeorge()~N )
and use it ( within ~b~Igeorge()~N ) as ~b~I(*sam)(a,b,c)~N.
If ~b~Isam()~N returns an ~b~Iint~N or ~b~Ichar~N then (of course)
it should be declared as ~b~Iint (*sam)()~N or ~b~Ichar (*sam)()~N!
.b2-14
.K16,32
mamma mia!
.WN
~r~IA root of x=f1(x) is 1.895475~N Here's
~r~IA root of x=f2(x) is 1.333324~N our
~r~IA root of x=f3(x) is 1.618026~N output.
and (because we use only ~Ifloat~N and not ~Idouble~N, and we gave
an error specification of .00005) we get (roughly) 4 decimal place
accuracy.
.W
Well, the programming ain't too sexy ( how useful are these 3 built-in
functions, f1(x), f2(x) and f3(x) ? ) and the mathematics is even worse
(you can't guarantee that the program won't get stuck in the ~b~Isolve()~N
function ... forever trying to reduce a growing error!), BUT ... we get
the idea ... right?
.WK16,32
RIGHT!!
.WN
.T
That's all folks!
.K16,32
au revoir!
.q