2031M Lab Exercise 4


Organization

You should create an appropriate directory for this lab like you did for the preceeding labs.

Submitting your work

We like to keep a record of who has done what and see general trends in submitted files. Remember that you will get feedback on your work only during the lab. For this lab, exercises 1 - 5 are for in-lab work only and don't need to be submitted (but you might just learn something by doing them). Submit programs q6.c and q7.c with the command
$ submit 2031 lab4 q6.c q7.c

The Exercises

NOTE that you don't have to do these in order.
  1. Explain and illustrate the preprocessor directives. For example
    #define MAX the-rest-of-the-line
    
    would cause the token MAX to be replace by the-rest-of-the-line in the source code before it is passed on to the next stage of compiling.

    Demonstration:

    Source Code:
                    #define MAX 255
                    #define MIN the rest of the line
    
                    int main(void){
    
                        int xMAX = MAX;
                        char A[MIN];
                        ...
                        return 0;
                    }
    
    Output of preprocessor (Use cc -E or cpp):
                    int main(void){
    
                        int xMAX =  255 ;
                        char A[ the rest of the line ];
                        ...
                        return 0;
                    }
    
    Note that the #define statements have disappeared and that the preprocessor did not object to the ... in the middle of the program.

  2. Parts of the program may be in different files, which can then be compiled together. Examine the file-to-file visibility of names; for example, can we define a function called "reverse" in both files? If we declare a variable int n in one file, can we use it in another file (can we declare another variable called n in another file?). So say/demonstrate something about the visibility of identifiers.

  3. Ansi C requires that a function prototype appear before a function call or if the function is defined in another file.

    Give examples where the prototype is missing and

    1. the program will not compile (but will compile when the prototype is included). You may want to leave this till you know what a struct is.

    2. the program will compile and everything works fine, despite the compiler warning.

    3. the program will compile and run but produces unexpected results that can be eliminated by including the prototype. You will probably need 2 files for this, with a function myFunction defined in one file and a main method calling myFunction in the other file. The prototype for myFunction belongs before main().

  4. When you write a program and try to compile and run it, you may be informed of errors at several stages:

    1. by the preprocessor,

    2. after the preprocessor has finished, but before the object (.o) file has been created,

    3. after the object file has been produced but before the executable (a.out) has been created.

    4. when the program is running


    Demonstrate such errors.

  5. After an object file is created (cc -c) you can get information about it using a variety of commands (try man -k "object file"). Take a look at the nm ("names") command and see what information it gives you about the names in an object file.

    (You do not need to understand all of the output from this command, just some of it.)

    To see the available binary utilities (binutils) like nm, see
        http://www.gnu.org/software/binutils/ 
    
    There is a link to documentation near the bottom of the page.

    NOTE: knowledge of these binutils is not a testable part of the course. This topic is included here for the interested student.

  6. Consider the following C program. It declares a swap function. Then it declares 2 ints, initializes them, prints them, swaps them and finally prints them out again.
    #include <stdio.h>
    
    void swap(int *a, int *b)
    {
        int temp = *a;
        *a = *b;
        *b = temp;
    }
    
    int main(void)
    {
        int x;
        int y;
    
        /* some code to assign x and y values */
        x = 256;
        y = 257;
    
        /* print out x and y before the swap */
        printf("Before swap: x = %d  y = %d\n", x, y);
    
        /* swap values of x and y */
        swap(&x, &y);
    
        /* print out x and y after the swap */
        printf(" After swap: x = %d  y = %d\n", x, y);
    
    
        return 0;
    }
    
    Copy, compile and run this program.

    How would you have to change this program if x and y were declared as char*** instead of int?

    First consider the program with "int" replaced with "TYPE" where appropiate (4 places). You would get

    #include <stdio.h>
    
    void swap(TYPE *a, TYPE *b)
    {
        TYPE temp = *a;
        *a = *b;
        *b = temp;
    }
    
    int main(void)
    {
        TYPE x;
        TYPE y;
    
        /* some code to assign x and y values */
        x = 256;
        y = 257;
    
        /* print out x and y before the swap */
        printf("Before swap: x = %d  y = %d\n", x, y);
    
        /* swap values of x and y */
        swap(&x, &y);
    
        /* print out x and y after the swap */
        printf(" After swap: x = %d  y = %d\n", x, y);
    
    
        return 0;
    }
    
    Of course, you may have to change the lines that assign x and y values, since the assignment given may not be appropriate for whatever TYPE is. And in the printf statements you may have to replace the %d with something else if TYPE isn't an int. (To print a pointer you use %p.)

    Note that you don't change the line that calls the swap. It is still
    swap(&x, &y);
    
    Finally, consider the program when you replace TYPE with char***.


  7. The following program declares and sorts 2 arrays using qsort - except that the calls to qsort are missing. First, compile and run the program. Be sure to use -Wall when compiling. Then supply the missing calls to qsort and repeat. You should get no warning messages when compiling.
        #include <stdlib.h>
        #include <stdio.h>
    
        /* prototype for the qsort function */
    
        /*
        This prototype is not necessary here since stdlib.h is #included.
        But it's here so you can see it and refer to it.
        */
    
        void qsort(void *base, size_t nmemb, size_t size,
                   int(*compar)(const void *, const void *));
    
        /* a comparison function for sorting into non-descending order */
        
        /*
        make sure you understand how intCompare1 starts with
        void * pointers p1 and p2 and gets int values for x and y from them.
        */
        
        int intCompare1(const void * p1, const void* p2) {
            int x = *((int *)p1);
            int y = *((int *)p2);
            if (x < y) return -1;
            if (x > y) return 1;
            return 0;
        }
    
        /* a comparison function for sorting into non-increasing order */
    
        /*
        intCompare2 has a formal parameter list different from the comparison
        function compar in the qsort prototype.  What to do ...? (Don't change it!)
        */
    
        int intCompare2(int * p1, int* p2) {
            int x = *p1;
            int y = *p2;
            if (x < y) return 1;
            if (x > y) return -1;
            return 0;
        }
    
        int main(void)
        {
            int i;
            int A[] = {2, 4, 9, 7, 9, 6, 4, 2, 5};
            int B[] = {5, 7, 8, 10, 22, 0, 2};
    
            /* print A */
            printf("A before sorting: ");
            for(i = 0; i < sizeof(A)/sizeof(int); i++)
                printf("%d ", A[i]);
            printf("\n");
    
            /* sort the last 4 elements of A into non-decreasing order using intCompare1*/
            /* don't hardcode length of A */
    
            /* ... */
    
            /* print A again */
            printf("A after sorting:  ");
            for(i = 0; i < sizeof(A)/sizeof(int); i++)
                printf("%d ", A[i]);
            printf("\n");
    
            /* print B */
            printf("\nB before sorting: ");
            for(i = 0; i < sizeof(B)/sizeof(int); i++)
                printf("%d ", B[i]);
            printf("\n");
    
            /* sort all of B into non-increasing order using intCompare2 */
            /* don't hardcode length of B */
    
            /* ... */
    
            /* print B again */
            printf("B after sorting:  ");
            for(i = 0; i < sizeof(B)/sizeof(int); i++)
                printf("%d ", B[i]);
            printf("\n");
    
            return 0;
        }
    
    

end of the exercises