Introductory C Programming

UW Experimental College

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 ints 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 ints,'' 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 rand function'':
```#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 n - m + 1.

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 randrange2 you already wrote,
```	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 / n
```
that is, if we were to write
```	rand() / (RAND_MAX / n)
```
then when rand returned RAND_MAX, the division could yield exactly n, which is one too many. (This wouldn't happen too often--only when rand returned that one value, its maximum value--but it would be a bug, and a hard one to find, because it wouldn't show up very often.) So if we add one to the denominator, that is, divide by the quantity
```	RAND_MAX / n + 1
```
then when rand returns RAND_MAX, the division will yield a number just shy of n, which will then be truncated to n - 1, which is just what we want. We add in 1, and we're done.

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 (n + 1, after adding in m), so we divide by
```	RAND_MAX / (n - m + 1) + 1
```

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 \t in the second-to-last call to printf prints a tab character, so that the histogram bars will all be lined up, regardless of the number of digits in the particular values of i or a[i]. Another possibility would be to use printf's width specifier (which we haven't really covered yet) to keep the digits lined up. That approach might look like this:
```			printf("%2d: %3d  ", i, a[i])
```