Programming Exercises

Here are some programming exercises for you to complete. New exercises will be added as the course progresses.

Relation to the marked labs and labtests

The marked labs, and the larger labtests, will be based pretty much on unmarked labs and on the material covered here. This list is cumulative; that is, for a marked lab you should have done all of the exercises posted before the lab.

The Exercises

  1. Write a C program called numberLines1.c. It should copy standard input to standard output a line at a time and number the output lines. Do not use the readlines function from the text; it is there to make certain things clear to the reader, not to replace existing C library functions. Use fgets and fputs (and anything else you need).

    The output lines should be numbered like this:
    0001 xxxxxxx
    0002 xxxxxxx
    ...
    0010 xxxxxxx
    0011 xxxxxxx
    ...
    0999 xxxxxxx
    1000 xxxxxxx
    ...
    
    You can assume that there are fewer than 10,000 lines in the file. The line number should be followed by a single blank, then by the line itself as shown above.

    For this exercise, do not use a large buffer for reading/writing. Use a buffer of size 20. You can assume that no line is longer than 20 characters, including the newline character '\n' at the end of the line.

  2. Write a C program called numberLines2.c. Its task is the same as that for the previous question, except that you cannot make any assumptions about the length of the lines that are being read. Nevertheless, you should still use a buffer of size 20.

  3. Write a C program called reverse.c. It should read standard input a (string) token at a time. Each token it reads should be output alone on a line, but written backwards, as in the following example (the dollar sign $ is the Bourne shell prompt).
    $ 
    $ cat someStrings 
    here
            are
                    some
    strings
    $ 
    $ reverse < someStrings 
    ereh
    era
    emos
    sgnirts
    $ 
    $ 
    
    The file reverse.c should contain a main method and a separate method void reverse(char string[]). The reverse function should not use any auxillary data structures; for example, it should not try to do anything like copying its string argument into an array and then copying the array backwards into the string.

    See the man page entry for the C-library strlen function.

  4.    Write a C program called palindrome.c that reads from standard input
       and writes to standard output.  Your program should copy to
       standard output all input lines that are palindromes (not
       counting the '\n' char at the end of the line, of course).
       No other output should be produced.
       You can assume that the input line does not contain the null
       char '\0'.  And you can assume that no input line is longer than 30 bytes, counting the 
       '\n' at the end.
       Note that the last line in a redirected input file may not end in '\n' (that depends on 
       the editor used to create the file).
    
    

  5.     On page 17 of the C text, you will find a program to
        copy files a byte at a time (OK - more exactly, to copy stdin
        to stdout a byte at a time).  We reproduce it here with
        line numbers as follows:
    
    
         1    #include 
         2
         3    int main( void )
         4    {
         5        int c;
         6
         7        while ((c = getchar()) != EOF)
         8            putchar(c);
         9
        10        return 0;
        11    }
    
        Carefully explain what is happening here.
        (a) Why is c declared int?
        (b) getchar() reads a byte from stdin, but returns an int - why/how?
        (c) putchar() takes an int parameter, but writes a single byte - why/how?
        (d) EOF has a value of -1 on our system, but it could have other values
            that would still allow the program here to function correctly.
            What range of values could EOF have? Explain.
        (e) What happens if c is declared as a char?  Show the results of a
            short experiment here (program, input file, output, explanation).
        (f) Like (e), but declare c as an unsigned char.
    
        Be sure to discuss any type conversions (you may wish to consult
        Appendix A of the C text.)
    
    

  6.     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):
    
                    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.
    

  7.     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
    
        (a) the program will not compile
            (but will compile when the prototype is included),
        (b) the program will compile and everything works fine,
        (c) the program will compile and run but produces unexpected
            results that can be eliminated by including the prototype.
    

  8.     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.
    
    

  9.     When you write a program and try to compile and run it, you may
        be informed of errors at several stages:
    
            (a) by the preprocessor,
            (b) after the preprocessor has finished,
                but before the object (.o) file has been created,
            (c) after the object file has been produced but before
                the executable (a.out) has been created.
            (d) when the program is running
    
        Demonstrate such errors.
    
    

  10.     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 ("name") 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.)
    
    

  11. Here's another simple I/O exercise. Write a C program that reads from standard input a line at a time. Assume no line is longer than 50 bytes. Copy to standard output all lines that contain exactly 2 tokens. See the man page for the scanf family of functions and pick something useful.

  12. Here are the exercises used in the first labtest: Labtest Questions
    The answers are actually quite short.

  13. Consider the following C program. It declares a swap function. Then 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***.


  14. 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 */
        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 */
        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 */
        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 */
            /* 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;
        }
    
    

  15. Here are the labtest2 exercises:
    labtest2b

End of the exercise