Comp111: Operating Systems
Classroom Exercise 6 Answers
Typedefs and Function Pointers
Fall 2017

In class we have briefly discussed advanced types in C, including const, function pointers, and typedefs. Let's explore pointers to function in more detail.

  1. If you are presented with the declarations
     
    typedef int *(*foo)(int); 
    foo bar; 
    
    With no further information, which of the following uses of foo are reasonable?
    1. bar(5)[2]=17;

      Answer: This is reasonable. bar(5) calls the function, which returns an int *, so that bar(5)[2] is of type int and is an lvalue (no const).
    2.  
      int *goo(int i) { 
          static int count=0; 
          count+=i; 
          return &count; 
      }
      ...
      bar=goo; 
      

      Answer: This is reasonable. goo is of type int (*)(int), so the assignment is type-conformal, and it returns the address of a static variable that contains a running counter hidden in the static variable count.
    3. void goo() { printf("yo!"); } 
      ...
      bar=goo; 
      

      Answer: This is not type-conformal. The function goo is of type void (*)(void) instead of int *(*)(int), but it will compile in most compilers! That doesn't mean it is not an error.
  2. What is the resulting type of cat after the following?
     
    typedef int dog[10]; 
    typedef dog cat[5]; 
    

    Answer: cat x; means dog x[5], which translates to int x[5][10]; the order of nesting is determined by sequential string substitution.
  3. Consider the following typedef:
     
    typedef int (*george)(const int *); 
    george rocks; 
    
    1. Write this without using a typedef.
      Answer:
      int (*rocks)(const int *); 
      
    2. Are the following uses reasonable? Why or why not?
      1.  
        int house(const int *x) { printf("x=%d\n",x); return *x; } 
        static int goo; 
        rocks=&house; 
        rocks(&goo); 
        

        Answer: house is already a pointer so that &house is nonsense, but our compiler will accept this nonsense. Otherwise it is fine.
      2.  
        int boat(const int *x) { (*x)++; return *x; } 
        extern int what; 
        rocks=boat;
        rocks(&what); 
        

        Answer: No. After declaring a const int *x, the function proceeds to modify it ((*x)++)! Otherwise it is fine.
  4. (Advanced) Explain exactly what happens when a program is distributed into two files that include the same header, and the header defines -- rather than declaring -- at least one object.
    Answer: The regular situation is that every globally accessible object (memory or function) in a program must have a unique name. If you define something in a header "foo.h" and then include it in "a.c" and "b.c" and then compile them into one program, the thing will be defined twice with the same name, which causes a linker error.

    Typedefs are a second problem. They cannot be executed twice because, by definition, the first iteration defines the undefined symbol!

  5. (Advanced) 'man signal' contains the following declarations:
     
    #include <signal.h>
    typedef void (*sighandler_t)(int);
    sighandler_t signal(int signum, sighandler_t handler);
    
    1. Write out the actual type of the function signal by expanding the typedefs.
      Answer:
       
      (void (*)(int)) signal(int signum, void (*handler)(int)); 
      
    2. Why will actually compiling this code result in a compiler error?
      Answer: The canonical answers is that the manual page lists the header file and then the contents of the header. The typedef is contained in the header. Thus it will be interpreted twice, which is a compiler error. That turns out not to be true in our brand new OS, and this compiles without error. This is probably because they specifically safeguarded the typedef in some way.