Tutorial Notes Week 5

Where we are:
------------

I spent the week talking about C. We have talked about basic data types,
preprocessor directives (include and define), and arrays and pointers.
We talked about passing arrays to functions.  On Wednesday I will talk
about dynamic memory allocation and pointers to pointers.

I talked briefly about strings in the day section, and more thoroughly in the night section.  (I'm about 1/2 lecture ahead in the night section.)

What to cover
-------------

I'd like you to go through some examples about pointers.  I've been really
pushing the memory model view of C.  They need to understand that a pointer
is a address into memory, and just because you can keep incrementing a
pointer doesn't mean it refers to a useful location in memory.

The syntax is still mysterious to many of the students.  Try not to let the
better students pull you ahead too quickly.

Some of the following questions were taken from King Chapter 11, page 218
I highly doubt you will get to the 2D array question. You may also want to leave
out some of the intermediate questions.  I'd really like you to do the strcpy 
example.

1. If i is a variable and p points to i, which of the following expressions
   are aliases for i?  (a and g)
   Get the students to tell you the meaning of each of the expressions.

   int i = 10;

   a) *p    - 10
   b) &p    - address of p 
   c) *&p   - address of i  (dereference the address of p)
   d) &*p   - address of i (get the address of what p points to)
   d) *i	- Error 'unary *'
   e) &i    - address of i
   g) *&i   - 10
   h) &*i   - Error 'unary *'

   Tell them that you shouldn't have to write code that uses constructions
   like c, d, g and h.

2. If i is an int variable and p and q are pointers to int, which of the
   following assignments are legal?

   int i = 20;
   
   a) p = i; warning: assignment makes pointer from integer without a cast 
             p holds 20 or 0x14 which is not a valid address.
   b) *p = &i; warning: assignment makes integer from pointer without a cast
             the location p points to gets the value of the address of i
             i == address of i now.
   c) &p = q; Error invalid lvalue (Can't change the address of p)
   d) p = &q; warning: assignment from incompatible pointer type
              the address of q is not of type pointer-to-int
   e) p = *&q; okay - p gets the value of q.
   f) p = q;   okay and much better syntax
   g) p = *q;  p is assigned what q points to. (q was not initialized)
   h) *p = q;  warning: assignment makes integer from pointer without a cast
   i) *p = *q;

Note: Draw pictures of the memory, to make this clear.

3. What are the bugs in this code?  (Need to dereference avg and sum.)



void avg_sum(double a[], int n, double *avg, double *sum)
{
    int i;
    sum = 0.0;
    for(i = 0; i < n; i++) 
	sum += a[i];
    avg = sum /n;


Solution:
void avg_sum(double a[], int n, double *avg, double *sum)
{
    int i;
    *sum = 0.0;
    for(i = 0; i < n; i++) 
	*sum += a[i];
    *avg = *sum /n;
	
}

Why would I pass in avg and sum as pointers? (We want to change their values, 
               and C uses pass-by-value for function arguments.) 
How would I call avg_sum? 

        double x[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        double sum;
        double avg;
        avg_sum(x, 10, &avg, &sum);

Why is the following incorrect?

        double x[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        double *sum;
        double *avg;
        avg_sum(x, 10, avg, sum);


4. (From King pg. 151) The compiler gives me an error is I try to copy one array into another.  What's wrong?
 a = b;  /* a and b are arrays */

You can't change the value of a or b since they name arrays.  Since "a" is 
really &a[0], you would be trying to change the address of a[0].

The simplest way to copy one array to another is to use a loop.
    for(i = 0; i < N; i++) 
       a[i] = b[i];

5. Re-write avg_sum using pointer notation instead of array notation.

void avg_sum(double a[], int n, double *avg, double *sum)
{
    int i;
    *sum = 0.0;
    for(i = 0; i < n; i++) {
	*sum += *a;
	a++;
    }
    *avg = *sum /n;
}

6. (King page 235) Suppose that the following declarations are in effect:

    int a[] = {5, 15, 34, 54, 14, 2, 52, 72};
    int *p = &a[1], *q = &a[5];

What is the value of the following expressions? (Please draw pictures.)

    printf("*(p+3) = %d\n" ,*(p+3));
    printf("*(q+3) = %d\n" ,*(q+3));
    printf("q-p = %d\n", q - p);
    printf("p<q = %d\n", p<q);
    printf("*p<*q = %d\n", *p<*q);

*(p+3) = 14
*(q+3) = 34
q-p = 4
p<q = 1 (true)
*p<*q = 0 (false)

7.  Do a couple of examples of how some string functions are implemented.  I'm
including implementations of strcpy and strncpy.  Also show them a few examples
of how to use strncpy, when it could go wrong.  Remind them that they need to
make sure that the destination pointer has memory allocated for it.

Get them to implement the function for you.  What kind of error checking could we
do? (We could check if a or b are NULL, but we can't do much else.)

char *
mystrcpy(char *a, char *b) 
{
  int i = 0;
  while( b[i] != '\0') {
    a[i] = b[i];
    i++;
  }
  a[i] = '\0';
  return a;
}

char *
mystrncpy(char *a, char *b, int n)
{
  int i;
  for(i = 0; i < n; i++) {
    a[i] = b[i];
    if(b[i] == '\0') {
      break;
    }
  }
  return a;

}

8. Two dimensional arrays.  Students should not have any problem writing code 
that uses a two-dimensional array.  They have seen them before in Java.  The
main difference in C is that we can use pointers to elements of the array.

I would be inclined to put the first loop up on the board and then change
it for each of the questions.  (You might have a more interesting example.)

#include <stdio.h>
#define N 4
#define M  3

int main()
{
    int a[N][M] = {{0, 1, 2}, 
		   {3, 4, 5},
		   {6, 7, 8},
		   {9, 10, 11}};
    int i, j;
    int *p = &a[0][0]; 

    /* print in row-major order */
    for(i = 0; i < N; i++) {
  	    for(j = 0; j < M; j++) {
	        printf("%d ", a[i][j]);
	    }
	    printf("\n");
    }
    printf("\n");

    /* print without using subscripts */
    for(i = 0; i < N; i++) {
	    for(j = 0; j < M; j++) {
	        printf("%d ", *(p++));
	    }
	    printf("\n");
    }
    printf("\n");
	
    /*transpose*/

    for(j = 0; j < M; j++) {
	    for(i = 0; i< N; i++) {
	        printf("%d ", a[i][j]);
	    }
	    printf("\n");
    }
    printf("\n");

    /* don't use array subscript notation*/
    p = &a[0][0]; 
    for(j = 0; j < M; j++) {
	    for(i = 0; i< N; i++) {
	        printf("%d ", *(int *)(p + i * M + j));
	}
	    printf("\n");
    }
    printf("\n");

    /* or if you can't remember the formula */
    p = &a[0][0]; 
    int *q  = p;;
    for(j = 0; j < M; j++) {
	    for(i = 0; i< N; i++) {
	        printf("%d ", *p);
	        p = p + M;
	    }
	    printf("\n");
	    q++;
	    p = q;
    }

    return 0;
}
