Lab overview
Lab 01
Lab 02
Lab 03
Lab 04
Lab 05
Lab 06
Lab 07
Lab 08
Lab 09
Lab 10
Lab 11
Lab 12
Lab 13
Review

[Course home page]

CSC 209 lab 10 exercises, week of 19 July 2022

Where to find solutions:

Most of the critique questions have the solutions linked-to below. The graded final item is solved and discussed in /u/csc209h/summer/pub/lab/soln/10/lab10.c


Attendance

As usual, please either run /u/csc209h/summer/present on the console of a lab workstation, or get the TA to record your attendance. Please do this first so that you don't forget.

Remember that you can check that your attendance has been recorded properly at https://wwwcgi.teach.cs.toronto.edu/~ajr/cgi-bin/auth/present


Part 1: C-preprocessor abuse

a) How does the program at /u/csc209h/summer/pub/lab/10/mess.c work? You don't need to analyze all of it; just try to analyze some of it. Test it first, of course. (Please don't write code like this yourself, except for fun! Never include such code in an assignment, or in any working code in real life either.)

b) Contrary to the parenthetical advice above, perhaps try writing something like this yourself.


Part 2: Critiquing and improving C code

The author of the following C code claims that it "works", but actually there are some problems with it.

Some of it does work, but is unnecessarily complicated or otherwise unnecessarily difficult to read. Your task for this lab is to simplify such code, maintaining its function.

Some of it works only in straightforward conditions but performs incorrectly in boundary cases or error conditions.

Example question 1:

Code excerpt:

void g()
{
    int d = x + 5;
    if (d < e) {
        [ten lines of code not involving 'd']
    }
    [twenty lines of code not involving 'd']
}

Solution:
Change "if (d < e)" to "if (x + 5 < e)" and get rid of the 'd' variable. This is simpler, but more importantly, with the original code the reader will be looking for where 'd' is used later on. And suppose, for example, that in future we need instead to compare "if (x + 6 < e)". Should we change d to "x+6" or will this cause trouble elsewhere? Unless this d value is very meaningful, the simpler version, without the additional variable, is much clearer for these sorts of reasons.

Question 2:

Problem:
Add three to the parameter.

Code:

int f(int x)
{
    int d = x + 3;
    return(d);
}

[solution] (the problem is repeated on the solution page, for each of today's problems)

Question 3:

Problem:
Open a file and terminate with an error if it doesn't exist:

Code:

    if (stat(file, &statbuf)) {
        printf("%s: No such file or directory\n", file);
        exit(1);
    }
    fp = fopen(file, "r");

[solution]

Question 4:

Problem:
Open a file and terminate with an error if it doesn't exist (same problem):

Code:

    if ((fp = fopen(file, "r")) == NULL) {
        perror("opening the file");
        exit(1);
    }

[solution]

Question 5:

Problem:
A function which returns true/false to indicate whether the argument exists and is a plain file (much like "test -f"):

Code:

int fexists(char *file)
{
    struct stat *p;
    p = malloc(sizeof(struct stat));
    return(stat(file, p) == 0 && S_ISREG(p->st_mode));
}

[solution]

Question 6:

Comment on the use of the cast in the following code. The "..." represents some number of lines of code.
statbuf.st_mtime might not be a long — it could be int, or even a non-standard type used by this particular compiler; but we want to store it in a variable and we have reason to believe that a variable of type "long" will be appropriate.

    long t;
    ...
    if (stat(filename, &statbuf)) {
        perror(filename);
        exit(1);
    }
    t = (long)statbuf.st_mtime;
    ...

[solution]

Question 7:

Problem:
Read a number from the file "foo", or return −1 if the file doesn't exist or doesn't begin with a number.

Code:

int foonumber()
{
    int x;
    FILE *fp = fopen("foo", "r");
    if (fp == NULL)
        perror("foo");
    if (fscanf(fp, "%d", &x) == 0)
        return(-1);
    return(x);
}

[solution] (there are at least three entirely separate problems with this code)

Question 8:

Problem:
"./a.out num1 num2" outputs the sum of the two numbers.

Code:

int main(int argc, char **argv)
{
    if (argv[2] == NULL) {
        printf("two integers required on the command line");
        return(55);
    }
    printf("%d", atoi(argv[1]) + atoi(argv[2]));
}

[solution] — but don't look at the solution after you've fixed just one problem -- there are at least five!


To submit:

Problem:
A simple cat-like program which opens a file whose name is on the command-line, and copies its contents to stdout.

Code:

#include <stdio.h>

int main(int argc, char **argv)
{
    FILE *fp;
    char c;

    if (argc < 2)
	printf("put a file name");

    fp = fopen(argv[1], "r+");
    while ((c = getc(fp)) > 0)
	putchar(c);
    fclose(fp);

    return(1);
}

You can find the above text in /u/csc209h/summer/pub/lab/10/lab10.c
Submit your improved program in a file named "lab10.c".

Your should not add or change anything which is not specifically to fix a defect in the above program. However, your program should behave like a proper unix program — for example, one of the defects above is the incorrect exit status. Its error handling and error message(s) are also wrong.

Programs which do not compile with "gcc −Wall" with no errors or warnings will not get the mark for this lab. However, this is not the only cleanup required. I count ten defects above (although it does depend on how you count), of which you must fix at least six (without introducing any new defects nor changing anything irrelevant) to get the mark.

Submit your file with

        submit -c csc209h -a lab10 lab10.c
, by the end of midnight at the end of Friday. and don't forget to run /u/csc209h/summer/present during the tutorial time, on the console of a tutorial lab workstation.

Q: What does "r+" do in the fopen()?
A: It opens for read-write — after that, it is possible either to read from or to write to the file. Thus this fails if the file is read-only.