Advanced Programming (CS3995) Spring 2002 -- Midterm Solutions

This is an open book, open notes exam. However, you may use only the two required textbooks: Advanced Programming in the Unix Environment and Code Complete: A Practical Handbook of Software Construction.

You have one hour and fiften minutes to complete the exam.

Your blue book must contain the problems in numerical order, with no more than one problem per page.

Statistics

Mean 66.7
Median 68
Min 33
Max 98
Std Dev 16.4
  1. [5] Describe briefly what happens to stdin, stdout and stderr in the Unix command
      cat data.txt | sort
    
    where data.txt contains lines of text.
    The stdout of the 'cat' command is redirected to the stdin of the 'sort' command via a pipe. stderr of both commands displays text to the terminal.
  2. [10] Given the definitions
      int a[10][20];
      int *b[10];
    
    then a[3][4] and b[3][4] are both syntactically legal references to a single int. What's the difference between the two definitions?
    The definition of a actually allocates storage for 10*20=200 integer elements. The definition for b allocates 10 pointers that can point to variable-length rows. No storage is allocated for these rows; this needs to be done separately, e.g., by malloc().
  3. [10] Explain what
      int *f(double *, ...);
      int (*g)(struct foo *);
    
    do. (E.g., int h(double) is a function returning an integer and taking a double as an argument, passing it by value.)

    f returns a pointer to an integer, based on a variable-length argument list. The first argument is a pointer to a double variable, so that the value is passed by reference.

    g is a function pointer to a function returning an integer. The function referenced by g takes a single argument, a pointer to a structure of type foo.

  4. [10] make: Construct a makefile that describes the process of making bread. Assume that your raw ingredients are seeds for growing wheat, water, yeast and salt. There should be at least three levels of dependencies; you can invent your own commands to reflect the bread making process.
    Wheat grows from seeds. Dough is made by mixing wheat flour, water, yeast and salt. The dough is shaped by hand or a machine. The dough is then left to rise when left alone in a warm surrounding. Finally, the loaf is baked.
    wheat: seeds water sun
       sow seeds
       wait 4 months
    
    flour: wheat
       grind wheat
    
    dough: flour water yeast salt
       mix flour water yeast salt
    
    bread: dough
       rise dough
       bake dough
    
  5. [10] For the following function,
      extern int p;
      int debug = 0;
    
      int fact(int x) {
        int p;
    
        if (debug) {
          printf("x = %d\n", x);
        }
        p = x * fact(x-1);
        return p;
      }
    
    describe where (i.e., type of memory) the variables are placed at run-time.
    The global variable p is an external global variable stored in the uninitialized data segment (assuming that the actual definition has no initial value). debug is stored in the initialized data segment. The local variables p and x are stored on the stack. (The program has the minor problem that it doesn't terminate, but that's beyond the scope of the question...)
  6. [20] Sketch a main function that prints a string "Still working (t)..." to the screen every ten seconds, while the rest of the program goes about its normal business. t is the number of seconds that the program has been active.
    In the program below, the sleeping and printing doing computation part could be replaced by any other computation. Re-setting the signal handler within the handler is required only on Solaris, not on Linux. The inclusion of signal() within handler() made no difference in grading.
    #include 
    #include 
    
    void handler(int sig) {
      printf("Still working...!\n");
      signal(SIGALRM, handler);  
      alarm(10);
    }
    
    int main(int argc, char *argv[]) {
      int i;
    
      signal(SIGALRM, handler);  
      alarm(10);
      while (1) {
        sleep(3);
        printf("doing computation\n");
      }
    
      return 0;
    }
    
  7. [25] Write a C routine that implements malloc(). You do not have to worry about optimization, but must use pointer and pointer arithmetic. Unlike in a real OS, you are given a large chunk of memory, defined as
      char memory[100000];
    

    You only have to implement malloc(), not free or other related functions. You can assume that a free() function exists. Your function should not run out of memory as long as there is available space in memory, either unallocated or freed. You do not have to worry about fragmentation and may return a larger chunk of memory than is necessary.

    malloc.c
  8. [10] What is a cvs checkin conflict? What would you suggest to minimize the chances for cvs checkin conflicts? (You can recommend cvs mechanisms and other approaches; you should present at least two recommendations.)
    A cvs checkin conflict occurs if two or more developers edit the same source file in roughly the same place. cvs cannot decide which of the changes should take precedence. The checkin conflict is flagged when the second person tries to check in the changes. There are several complementary ways to avoid or minimize this problem:
    • Frequent cvs update to make sure that one is working with the most recent version.
    • Use of cvs watch and cvs edit to alert other developers in the group that one is working on a particular file.

Last updated by Henning Schulzrinne