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 }