Computer Science 3 - 2001 - Test on Prac 15

(This should take no longer than 15 minutes and is simply intended
to probe whether you have understood the material in the prac questions.
Answer on the question sheet.  You may use your C library summary.)

THURSDAY TEST:

2. What, if anything, would be the output of the program below of it were to
   be compiled from source in a file named P2.CPP and then executed with
   a DOS command     P2 <P2.CPP.    If there is no output, explain why.

          #include <stdio.h>
          void main (void) {
            char *s = new char[200];
            char *t = new char[200];
            s = gets(s);
            t = s;
            s = gets(s);
            printf("s = %s t = %s", s , t);
          }

The output is

          s = void main (void) { t = void main (void) {

The point that is easily missed is that the assignment s = t does not copy a
string (you would need strcpy(t, s) to do that).  It simply points t to the
same storage as is pointed to by s.  Hence the second gets(s) call stores the
second line of the data (which in this case was the second line of the
program source text), and leaves s and t both pointing to it.

Severe misconceptions observed in solutions were that the program would do
nothing because it had no data.  This was surprising; if you had done the prac
properly you should have known all about input redirection using the <
symbol.  Similarly, there were some unbelievable claims that there would be no
output because there was no output file.  All programs like this have three
files open "by default" - their "file pointers" are stdin, stdout and stderr -
and simple calls to printf, gets, puts and so on work in terms of these files.
That is what makes simple programs so simple - but maybe this is all lost on
people who have been raised in an almost totally "GUI" environment?

3. Four students try reading a character from standard input.  One student
   uses the function call

          char ch = getchar();

   The others each use a different one of the other available functions.  What
   do their function calls look like?

There are a whole heap of possibilities here:

          char ch = fgetchar();
          char ch = getc(stdin);
          char ch = fgetc(stdin);
          scanf("%c", &ch);
          fscanf(stdin, "%c", &ch);

4. A student came up with the following simple program for copying standard
   input to standard output.   Did it do the job correctly?  If not, why not,
   and how would you fix it in that case?

        #include "misc.h"

        void main (void) {
        // copy stdin to stdout
        // Normal usage:  TASK1 <infile >outfile
        // Ima Geek, Rhodes University, 2001
          char ch;
          do
            { putchar(ch = getchar()); }
          while (ch != EOF);
        }

It does not work properly as it will copy the EOF "character" to the output.
A correct version would require something like

        void main (void) {
          char ch = getchar()); }
          while (ch != EOF) { putchar(ch); ch = getchar(); }
        }

A worrying misconception that some students have related to the statement

            putchar(ch = getchar());

which they claimed would have nothing to "put".  That is not correct.  In C and C++
there is no "assignment statement" as such.  A "statement" of the form

            target = b + c;

is really an "expression" which has the value that results from adding b to c.
Almost as a "side effect" this value is also assigned to target.  Of course,
in most programmers' way of thinking it is the assignment that is the
important part of all this.  So in the above statement we assign the value
read by getchar to the variable ch, and this value is taken as the value of
the expression that is then passed to the putchar function as an argument.

This is all rather different from the way Pascal-style programmers think!


5. Write a C++ program that will act as a simple calculator and add its
parameters and display the total.  For example, if the program is compiled from
source in the file TEST5.CPP and run from DOS with a command like

           TEST5  1  2  3  4

it should produce the output  10,  if run with the command

           TEST5  123 345

it should produce the output  468  (since 123+345 = 468, most times!)

One point several people missed was that input has to come from parsing the
command line parameters, not stdin; another point missed was that the strings
have to be converted to numbers before they are added.  A minimal solution
follows:

  #include "misc.h"

  void main (int argc, char *argv[]) {
  // Calculate sum of (integer) command line arguments
    int total = 0;
  // check correct command line usage - very wise to do this
    if (argc < 2) {
      fprintf(stderr, "Usage: TEST5 n1 n2 ... \n"); exit(1);
    }
    for (int i = 1; i < argc; i++) total += atoi(argv[i]);
    printf("Total is %d", total);
  }

FRIDAY TEST:

2. What, if anything, would be the output of the program below if it were to
   be compiled from source in a file named P2.CPP and then executed with
   a DOS command     P2 <P2.CPP.    If there is no output, explain why.

         #include "misc.h"
         void main (void) {
           char ch;
           for (int i = 1; i < 5; i++)
           { for (int j = 1; j <= 3; j++)
             { ch = getchar();
               if (isalpha(ch)) putchar(ch);
             }
             ungetc(toupper(ch), stdin);
           }
         }

The output is   inNclLudDe.    Four groups of 3 characters are read from the
first line of the input (which is the program code itself).  However, every
third character is poked back into the input stream after being converted to
UPPER CASE.  Had you done the Clang beautifier exercise properly you would
have known all about ungetc!

3. Four students try reading a single character and copying it to standard
   output.  One student uses the function calls

          char ch = getchar();
          putchar(ch);

   The others each use getchar, followed by a different one of the other
   available functions for output.  What do their function calls look like?

There are a whole heap of possibilities here:

          fputchar(ch);
          fputc(ch, stdout);
          putc(ch, stdout);
          printf("%c", ch);
          fprintf(stdout, "%c", ch);

4. A student came up with the following simple program for finding the longest
   line in a data file supplied as standard input.   Did it do the job
   correctly?  If not, why not, and how would you fix it in that case?

        #include "misc.h"
        void main (void) {
        // read stdin and find the longest line
        // Normal usage:  TASK1 <infile >outfile
        // Ima Geek, Rhodes University, 2001
          char* nextLine, longestLine;
          int longestLength = 0;
          nextLine = gets(nextLine);
          while (!EOF) {
            if (strlen(nextLine) > longestLength) longestLine = nextLine;
            nextLine = gets(nextLine);
          }
          puts("Longest line was "); puts(longestLine);
        }

Oh dear.  The code is a disaster; so were the solutions received.  Here is a
corrected solution, with commentary:

        #include "misc.h"
        void main (void) {
        // read stdin and find the longest line
        // Normal usage:  TASK1 <infile >outfile
        // Ima Geek, Rhodes University, 2001
          char * nextLine = new char[1000];
          char * longestLine = new char[1000];     // *** allocate storage
          int nextLength;                          // *** another variable
          int longestLength = 0;
          nextLine = gets(nextLine);
          while (nextLine != NULL) {               // *** correct test
            nextLength = strlen(nextLine);         // *** compute once
            if (strlen(nextLine) > longestLength) {
              strcpy(longestLine, nextLine);       // *** copy correctly
              longestLength = nextLength;          // *** update
            }
            nextLine = gets(nextLine);
          }
          puts("Longest line was "); puts(longestLine);
        }

5. Here is a snippet of a C++ program that is intended to copy a text file.

      int main(void) {
      // Copy one file to another
        FILE *infile, *outfile;
        char ch;
        infile = fopen("input", "r");
        outfile = fopen("output", "w");
        while (!feof(infile)) {
          fprintf(outfile, "%c", ch);
          fscanf(infile, "%c", ch);
        }
      }

   Although it compiles, this program will not work properly.  What minimal
   changes would make it "work".

   It also contains at least one example of very poor programming practice.
   Can you identify this and show how the program should be improved?

Oh dear.  And you had all done pracs like this all week!

      int main(void) {
      // Copy one file to another
        FILE *infile, *outfile;
        char ch;
        infile = fopen("input", "r");
        if (infile == NULL) {           // must check
          fprintf(stderr, "cannot open input\n"); exit(1);
        }
        outfile = fopen("output", "w");
        if (outfile == NULL) {          // must check
          fprintf(stderr, "cannot open output\n"); exit(1);
        }
        fscanf(infile, "%c", &ch);      // must read the first char, needs &
        while (!feof(infile)) {
          fprintf(outfile, "%c", ch);
          fscanf(infile, "%c", &ch);    // needs &ch
        }
        if (fclose(outfile) == EOF) {
          fprintf(stderr, "could not close output\n"); exit(1);
        }
        // there is no damage caused if one does not close input files
        return(EXIT_SUCCESS);           // main needs to return a value
      }