CS140 Lecture notes -- Some more on Malloc and Fields

  • Jim Plank
  • Directory: /home/plank/cs140/Notes/MalField
  • Lecture notes: http://www.cs.utk.edu/~plank/plank/classes/cs140/Notes/MalField
  • Fri Aug 31 13:06:39 EDT 2007

    A simple malloc() example

    Try to write the following program: variance. Variance takes one command line argument, which is a number n. Variance expects to read n floating point numbers on standard input. It will then calculate the average of these numbers, and then for each of the n numbers, the square of the difference between that number and the average. The variance is the average of these values. It will print out the average and the variance.

    In other words, suppose n is 3 and the numbers are 1, 2, and 3. Then the average is 2. The squares of the differences are 1, 0 and 1, so the variance is 2/3.

    Suppose n is 3 and the numbers are 10, 5 and 3. The average is 18/3 = 6. The squares of the differences are (10-6)(10-6) = 16, (5-6)(5-6) = 1, and (3-6)(3-6) = 9. Thus, the variance is (16+1+9)/3 = 26/3 = 8.6667.

    Ok, now take a few minutes and try to write variance.

    Here's the strategy for writing variance

    And here's the code (also in variance.c):

    #include <stdio.h>
    
    int main(int argc, char **argv)
    {
      int n, i;
      double *values;
      double avg;
      double variance;
    
      
      /*  First you need to get n from the command line arguments. */
      
      if (argc != 2) {
        fprintf(stderr, "usage: variance n\n");
        exit(1);
      }
      n = atoi(argv[1]);
      if (n <= 0) exit(1);
    
      /*  Next, you need to malloc() space for n doubles.   */
    
      values = (double *) malloc(sizeof(double)*n);
    
      /*  Next, you read them in using scanf(). */
    
      for (i = 0; i < n; i++) {
        if (scanf("%lf", &(values[i])) != 1) exit(1);
      }
    
      /*  Next, you compute their average. */
    
      avg = 0;
      for (i = 0; i < n; i++) {
        avg += values[i];
      }
      avg /= n;
    
      /*  Now, you compute the sum of the squares of the differences. */
    
      variance = 0;
      for (i = 0; i < n; i++) {
        variance += ((values[i]-avg)*(values[i]-avg));
      }
    
      /*  Finally, you compute the variance and print them both out. */
    
      variance /= n;
    
      printf("Average:  %lf\n", avg);
      printf("Variance: %lf\n", variance);
      return 0;
    }
    

    It works quite nicely:

    UNIX> variance 
    usage: variance n
    UNIX> variance 3
    1 2 3
    Average:  2.000000
    Variance: 0.666667
    UNIX> variance 3
    1
           2                    3
    Average:  2.000000
    Variance: 0.666667
    UNIX> variance 3
    10
    
    
    5
             3
    Average:  6.000000
    Variance: 8.666667
    UNIX> 
    

    Different input

    Now, suppose that the input can have comment lines. If a line begins with the '#' character, then it should be ignored. This throws a real monkey wrench into scanf() -- you would have a very hard time doing this with scanf(). Why? Because if you're using scanf("%lf", ...), you are basically ignoring line information. So how can you figure out if a line begins with '#'?

    We'll solve this by using the fields library instead. We'll read in lines using get_line() and if a line begins with '#', we'll ignore it. If it doesn't, then we'll use sscanf() to turn its fields into double's. The code is in variance2.c. Here is the only change from variance.c:

    #include "fields.h"
    
    main(int argc, char **argv)
    {
      int n, i, j;
      IS is;
       
      ...
      
      /*  Next, you read them in using the fields library. */
    
      is = new_inputstruct(NULL);
      i = 0;
      while (i < n && get_line(is) >= 0) {
        if (is->text1[0] != '#') {
          for (j = 0; j < is->NF; j++) {
            if (i < n) {
              if (sscanf(is->fields[j], "%lf", &(values[i])) != 1) exit(1);
              i++;
            }
          }
        }
      }
      if (i < n) exit(1);
    

    Note, I've covered for some common input errors such as not entering a double, and not entering enough numbers. Go over this code until you understand exactly what it is doing. I will expect you to be able to write code like this without problems.

    When we run it, we see it works:

    UNIX> make variance2
    gcc -g -I/home/cs140/include -c variance2.c
    gcc -g -I/home/cs140/include -o variance2 variance2.o /home/cs140/objs/fields.o
    UNIX> variance2 3
    1 2 3
    Average:  2.000000
    Variance: 0.666667
    UNIX> variance2 3
    1 2 Frog
    UNIX> variance2 3
    10
    # Hi!!!!
    5
    3
    Average:  6.000000
    Variance: 8.666667
    UNIX> 
    
    Note, when I typed Frog, it exited, because sscanf() returned zero.