UW Experimental College

Assignment #3 ANSWERS

Question 1.
*How many elements does the array
*`int a[5]
`*contain?
Which is the first element?
The last?
*

The array has 5 elements.
The first is `a[0]`;
the last is `a[4]`.

Question 2.
*What's wrong with the scrap of code in the question?
*

The array is of size 5, but the loop is from 1 to 5,
so an attempt will be made to access the nonexistent element `a[5]`.
A correct loop over this array would run from 0 to 4.

Question 3.
*How might you rewrite the dice-rolling program
without arrays?
*

About all I can think of would be to declare 11 different variables:

int roll2, roll3, roll4, roll5, roll6, roll7, roll8, roll9, roll10, roll11, roll12; sum = d1 + d2; /* sum two die rolls */ if(sum == 2) roll2 = roll2 + 1; else (sum == 3) roll3 = roll3 + 1; else (sum == 4) roll4 = roll4 + 1; else (sum == 5) roll5 = roll5 + 1; ...What a nuisance! (Fortunately, we never have to write code like this; we just use arrays, since this sort of situation is exactly what arrays are for.)

Question 4.
*What is the difference between a defining instance and an external declaration?
*

A defining instance is a declaration of a variable or function that actually defines and allocates space for that variable or function. In the case of a variable, the defining instance may also supply an initial value, using an initializer in the declaration. In the case of a function, the defining instance supplies the body of the function.

An external declaration is a declaration which mentions the name and type of a variable or function which is defined elsewhere. An external declaration does not allocate space; it cannot supply the initial value of a variable; it does not need to supply the size of an array; it does not supply the body of a function. (In the case of functions, however, an external declaration may include argument type information; in this case it is an external prototype declaration.)

Question 5.
*What are the four important parts of a function?
Which three does a caller need to know?
*

The name, the number and type of the arguments, the return type, and the body. The caller needs to know the first three.

Tutorial 3.
*Modify the array-of-squares program to also print cubes.
*

#include <stdio.h> int main() { int i; int squares[11]; /* [0..10]; [0] ignored */ int cubes[11]; /* fill arrays: */ for(i = 1; i <= 10; i = i + 1) { squares[i] = i * i; cubes[i] = i * i * i; } /* print table: */ printf("n\tsquare\tcube\n"); for(i = 1; i <= 10; i = i + 1) printf("%d\t%d\t%d\n", i, squares[i], cubes[i]); return 0; }

Tutorial 4.
*Rewrite the simple graphics program to print ``open'' boxes.
*

I made a new version of the original `printsquare` function
called `printbox`, like this:

int printbox(int n) { int i, j; for(j = 0; j < n; j = j + 1) printf("*"); printf("\n"); for(i = 0; i < n-2; i = i + 1) { printf("*"); for(j = 0; j < n-2; j = j + 1) printf(" "); printf("*\n"); } for(j = 0; j < n; j = j + 1) printf("*"); printf("\n"); return 0; }A box of size 1 or 2 doesn't have all three parts. If you want the function to handle those sizes more appropriately, here are the necessary modifications:

int printbox(int n) { int i, j; for(j = 0; j < n; j = j + 1) printf("*"); printf("\n"); for(i = 0; i < n-2; i = i + 1) { printf("*"); for(j = 0; j < n-2; j = j + 1) printf(" "); if(n > 1) printf("*\n"); } if(n > 1) { for(j = 0; j < n; j = j + 1) printf("*"); printf("\n"); } return 0; }

Exercise 1.
*Write code to sum the elements of an array of int.
*

Here is a little array-summing function:

int sumnum(int a[], int n) { int i; int sum = 0; for(i = 0; i < n; i = i + 1) sum = sum + a[i]; return sum; }Here is a test program to call it:

#include <stdio.h> int a[] = {1, 2, 3, 4, 5, 6}; int sumnum(int [], int); int main() { printf("%d\n", sumnum(a, 6)); return 0; }

Exercise 2.
*Write a loop to call the multbytwo function
on the numbers 1-10.
*

#include <stdio.h> int multbytwo(int); int main() { int i; for(i = 1; i <= 10; i++) printf("%d %d\n", i, multbytwo(i)); return 0; }

Exercise 3.
*Write a square() function
and use it to print the squares of the numbers 1-10.
*

The squaring function is quite simple:

int square(int x) { return x * x; }Here is a loop and main program to call it:

#include <stdio.h> int square(int); int main() { int i; for(i = 1; i <= 10; i = i + 1) printf("%d %d\n", i, square(i)); return 0; }

Exercise 4.
*Write a printnchars function,
and use it to rewrite the triangle-printing program.
*

Here is the function:

void printnchars(int ch, int n) { int i; for(i = 0; i < n; i++) printf("%c", ch); }Here is the rewritten triangle-printing program:

#include <stdio.h> int main() { int i; for(i = 1; i <= 10; i = i + 1) { printnchars('*', i); printf("\n"); } return 0; }

Exercise 5.
*Write a function to compute the factorial of a number,
and use it to print the factorials of the numbers 1-7.
*

Here is the function:

int factorial(int x) { int i; int fact = 1; for(i = 2; i <= x; i = i + 1) fact = fact * i; return fact; }Here is a driver program:

#include <stdio.h> int factorial(int); int main() { int i; for(i = 1; i <= 7; i = i + 1) printf("%d %d\n", i, factorial(i)); return 0; }

The answer to the ``extra credit'' problem is that to
portably compute factorials beyond `factorial(7)`,
it would be necessary to declare the `factorial()`
function as returning `long int`
(and to declare its local `fact` variable as
`long int` as well,
and to use `%ld` in the call to `printf`).
8! (``eight factorial'') is 40320,
but remember,
type `int` is only guaranteed to hold integers up to 32767.

(Some machines, but not all,
have `int`s that can hold more than 32767,
so computing larger factorials on those machines would happen to work,
but not portably.
Some textbooks would tell you to
``use `long int`
if your machine has 16-bit `int`s,''
but why write code two different ways depending on what kind of
machine you happen to be using today?
I prefer to say,
``Use `long int`
if you would like results greater than 32767.'')

Exercise 6.
*Write a function celsius()
to convert degrees Fahrenheit to degrees Celsius.
Use it to print a Fahrenheit-to-Centigrade table
for -40 to 220 degrees Fahrenheit, in increments of 10 degrees.
*

Here is the function:

double celsius(double f) { return 5. / 9. * (f - 32); }Here is the driver program:

#include <stdio.h> double celsius(double); int main() { double f; for(f = -40; f <= 220; f = f + 10) printf("%f\t%f\n", f, celsius(f)); return 0; }

Exercise 7.
*Modify the dice-rolling program
so that it computes the average
(and, optionally, the standard deviation)
of all the rolls of the pair of dice.
*

The new code involves declaring new variables
`sum`, `n`, and `mean`
(and, for the extra credit problem, `sumsq` and `stdev`),
adding code in the main dice-rolling loop to update `sum` and `n`
(and maybe also `sumsq`),
and finally adding code at the end to compute the mean
(and standard deviation)
and print them out.

#include <stdio.h> #include <stdlib.h> #include <math.h> int main() { int i; int d1, d2; int a[13]; /* uses [2..12] */ double sum = 0; double sumsq = 0; int n = 0; double mean; double stdev; for(i = 2; i <= 12; i = i + 1) a[i] = 0; for(i = 0; i < 100; i = i + 1) { d1 = rand() % 6 + 1; d2 = rand() % 6 + 1; a[d1 + d2] = a[d1 + d2] + 1; sum = sum + d1 + d2; sumsq = sumsq + (d1 + d2) * (d1 + d2); n = n + 1; } for(i = 2; i <= 12; i = i + 1) printf("%d: %d\n", i, a[i]); printf("\n"); mean = sum / n; stdev = sqrt((sumsq - sum * sum / n) / (n - 1)); printf("average: %f\n", mean); printf("std. dev.: %f\n", stdev); return 0; }

Exercise 8.
*Write a randrange function.
*

Here is a straightforward implementation
of `randrange2`:

#include <stdlib.h> int randrange2(int m, int n) { return rand() % (n - m + 1) + m; }Here is one using the suggested ``better way of reducing the range of the

#include <stdlib.h> int randrange2(int m, int n) { return rand() / (RAND_MAX / (n - m + 1) + 1) + m; }Notice that I've replaced N in the suggested general form with the expression

You could implement the simpler `randrange` function either as

int randrange(int n) { return rand() % n + 1; }or, using the improvement,

int randrange(int n) { return rand() / (RAND_MAX / n + 1) + 1; }or, by writing it ``on top of'' the more general

int randrange(int n) { return randrange2(1, n); }

The various ``fudge factors'' in these expressions
deserve some explanation.
The first
one is straightforward:
The `+ 1` in `(n - m + 1)`
simply gives us the number of numbers in the range `m` to
`n`, *including* `m` and `n`.
(Leaving out the `+ 1` in this case is the classic
example of a fencepost error,
named after the old puzzle,
``How many pickets are there in
a picket fence ten feet long, with the pickets one foot apart?'')

The other `+1` is a bit trickier.
First let's consider the second implementation of `randrange`.
We want to divide `rand`'s output by some number
so that the results will come out in the range 0 to `n - `1.
(Then we'll add in 1 to get numbers in the range 1
through `n`.)
Left to its own devices,
`rand` will return numbers in the range 0 to `RAND_MAX`
(where `RAND_MAX` is a
constant defined for us in `<stdlib.h>`).
The division, remember, is going to be integer division,
which will truncate.
So numbers which would have come out in the range
0.0 to 0.99999... (if the division were exact) will all truncate to 0,
numbers which would have come out in the range
1.0 to 1.99999... will all truncate to 1,
2.0 to 2.99999... will all truncate to 2,
etc.
If we were to divide `rand`'s output
by the quantity

RAND_MAX / nthat is, if we were to write

rand() / (RAND_MAX / n)then when

RAND_MAX / n + 1then when

In the case of the more general `randrange2`,
everything we've said applies,
with `n` replaced by `n - m + 1`.
Dividing by

RAND_MAX / (n - m + 1)would occasionally give us a number one too big (

RAND_MAX / (n - m + 1) + 1instead.

Finally, just two lines in the dice-rolling program would need to be changed to make use of the new function:

d1 = randrange(6); d2 = randrange(6);or

d1 = randrange2(1, 6); d2 = randrange2(1, 6);

The answer to the extra-credit portion of the exercise
is that under some compilers,
the output of the `rand` function
is not quite as random as you might wish.
In particular, it's not uncommon for the `rand` function
to produce alternately even and odd numbers,
such that if you repeatedly compute `rand % 2`,
you'll get the decidedly non-random sequence 0, 1, 0, 1, 0, 1, 0, 1... .
It's for this reason that
the slightly more elaborate range-reduction techniques
involving the constant `RAND_MAX`
are recommended.

Exercise 9.
*Rewrite the dice-rolling program to also print a histogram.
*

#include <stdio.h> #include <stdlib.h> int main() { int i; int d1, d2; int a[13]; /* uses [2..12] */ for(i = 2; i <= 12; i = i + 1) a[i] = 0; for(i = 0; i < 100; i = i + 1) { d1 = rand() % 6 + 1; d2 = rand() % 6 + 1; a[d1 + d2] = a[d1 + d2] + 1; } for(i = 2; i <= 12; i = i + 1) { printf("%d: %d\t", i, a[i]); printnchars('*', a[i]); printf("\n"); } return 0; }The

printf("%2d: %3d ", i, a[i])

This page by Steve Summit // Copyright 1995-9 // mail feedback