Journal Articles

CVu Journal Vol 16, #4 - Aug 2004 + Student Code Critiques from CVu journal.
Browse in : All > Journals > CVu > 164 (12)
All > Journal Columns > Code Critique (70)
Any of these categories - All of these categories

Note: when you create a new publication type, the articles module will automatically use the templates user-display-[publicationtype].xt and user-summary-[publicationtype].xt. If those templates do not exist when you try to preview or display a new article, you'll get this warning :-) Please place your own templates in themes/yourtheme/modules/articles . The templates will get the extension .xt there.

Title: Student Code Critique Competition 29

Author: Administrator

Date: 03 August 2004 13:16:07 +01:00 or Tue, 03 August 2004 13:16:07 +01:00

Summary: 

Body: 

Prizes provided by Blackwells Bookshops & Addison-Wesley

Please note that participation in this competition is open to all members. The title reflects the fact that the code used is normally provided by a student as part of their course work.

This item is part of the Dialogue section of C Vu, which is intended to designate it as an item where reader interaction is particularly important. Readers' comments and criticisms of published entries are always welcome.

Before We Start

Thanks to the helping hand given by our editor and by a member of the committee, I've been able to get my hands on the only two entries for the current issue. It's quite sad to receive collaboration from people who contribute to ACCU in many other ways, and not from you. Being a 1000+ members association, if roughly 0.5% of the members participated, there could be plenty of material to provide food for thought. Please let us change this statistic for the better.

Remember that you can get the current problem set in the ACCU website (http://www.accu.org/journals/). This is aimed at people living overseas who get the magazine much later than members in the UK and Europe.

Late submission to SCC 27

From Tony Houghton

Let's start by solving the immediate problem. If pf() encounters a prime number it returns an "empty" array i.e. the first element is zero. This means that the body of the while loop in main() is not executed and the ugly flag is not altered; it retains its value from the previous number in the range, which is often ugly, at least for low ranges. The solution is therefore to reset the ugly flag to zero in each iteration of the enclosing for loop. Immediately before or after the line idx = 0; is ideal.

However, the code is still not performing the correct test. It only proves whether or not the last factor in the array is ugly, not that they all are. This may work with the defined set of ugly factors and because of the order in which pf() fills its arrays, but we should rewrite the test instead of relying on this. It's easiest to prove that a number is not ugly, so we'll start by assuming it is until we find a non-ugly factor, not forgetting the initial problem with primes.

But we have something else to take into account: 2, 3 and 5 are prime, so pf() will return an "empty" array and the test will not realise they're also ugly. We could deal with this in pf() by copying quotient's initial value from the variable number rather than 0, but this leads to an inconsistency when the pf() function is considered in its own right: prime numbers have themselves listed as a factor, other numbers don't.

As we're also excluding the number 1 as a factor it makes sense to continue to exclude the number itself and explicitly check for 2, 3 and 5 in our test. Thus the main for loop becomes:

for(n = start; n < stop + 1; ++n) {
  idx = 0;
  flist = pf(n);
  if(flist[0] == 0 && n > FIVE) ugly = 0;
  else ugly = 1;
  while(flist[idx]) {
    if(flist[idx] != TWO &&
       flist[idx] != THREE &&
       flist[idx] != FIVE) {
      ugly = 0;
    }
  ++idx;
  }
  if(ugly == 1)
    printf("%d\n", n);
  free(flist);
}

Now to give the code a complete makeover, starting from the top and working down:

The first issue we encounter is a number of preprocessor macros. Macros should only be used for jobs that nothing else can do, but these can be replaced with const int and/or enum. Furthermore, the purpose of replacing "magic numbers" with named constants is so that if the values need to be changed they only need to be changed in one place. If we want to change this program to deal with a different set of ugly factors, the current names of the constants will be very confusing; if we don't ever want to change it the names are redundant. I've rewritten these constants as:

const int MaxFactors = 20;
  /* Max number of prime factors to find */
enum UglyFactors {
  UglyFac1 = 2,
  UglyFac2 = 3,
  UglyFac3 = 5
};

(Sorry about the pun).

Next we have a prototype for pf(). The name is far too terse, let's make it more descriptive: prime_factors(). The function is only used in the same source file, so it should be declared static. I'm not sure whether the student has covered linkage types yet, but I think it's a relatively straightforward concept and a good habit to learn early.

It appears to be common practice to declare all function prototypes in advance and define static functions towards the end of a file. However, I prefer to avoid separate prototypes except in headers on the grounds that they are a form of repetition. Therefore I've brought the body of the function forward to this point. The comment that precedes it should highlight any points of interest about its parameters and return value - in this case that it returns an array that's been allocated on the heap and that the array is zero-terminated.

Inside the function I've separated the variable declarations because int* and int are actually two quite different types and it's considered bad form to make them share the declaration with commas. I also prefer to give all variables their own distinct declarations unless two or more are closely related and have no initialisers.

I also decided to use the variable name i instead of idx. There is an argument for short variable names as well as long descriptive ones, and while idx was a good name to start with, being concise but still meaningful to almost any programmer, I always use i for the (primary) array index in loops myself and sticking to that convention makes the code more readable to me.

I've changed the while loop condition to (divisor <= number); I think it's less clutter than the original. But we should also check that we haven't exceeded the array bounds. If we really want to know all the prime factors we should extend the array when necessary or come up with a rough figure that's guaranteed to be an overestimate - e.g. the upper limit of the range given to the program - and make the array this size in the first place. However, for this purpose it's adequate to return when the array is filled, remembering to leave room for the terminating zero - which does not need to be inserted explicitly because of the use of calloc instead of malloc. The complete condition is now (divisor <= number && i < MaxFactors - 1).

I noticed that it's possible for the same factor to be entered more than once in the list if its square is also a factor e.g. 2 will appear twice if number is 8 or 12. We can prevent this by checking whether the previous entry is equal to the one we're about to add - remembering that if i is 0 we mustn't try to check element-1:

if(i == 0 || flist[i - 1] != divisor)
  flist[i++] = divisor;

I also have a rule about braces around single statements such as a simple if or for body. Although these are optional I often include them, especially if:

  1. the parent statement, e.g. an if conditional, is long and I've wrapped it to fit my editor window - the indentation makes it difficult to distinguish the conditional from the body otherwise

  2. it's an if or else clause and its sibling clause needs braces

  3. there's any possibility that the statement is a macro which could be switched off e.g. an assertion or extra logging in debug builds. I mention all this because I've applied rule (2) to the else statement here

Next I've done some refactoring. Breaking programs down into smaller functions almost invariably makes them more readable, and providing a function to test whether a given number is ugly is a logical step. The body of this function is pretty much as above except that I replaced the while loop with a for loop. Also, bearing in mind being able to change the values of the UglyFac constants, it's unsafe to assume that there are no non-ugly primes below UglyFac3, so we should test against each of them instead of testing whether n > UglyFac3.

I've given main() a comment about its arguments, which is basically an alternative to the "enter a range" comment which I thought was misleading: it implied the range would be read from stdin rather than arguments. Again I've separated the variable declarations. I've kept the variable name n because it's ideal for a loop variable describing a number other than an index, but I've renamed start and stop to first and last, which I think makes it clearer that the range is inclusive. The for loop terminating condition is again changed to use <= instead of <

Just a couple of minor semantics remaining. As ugly is a boolean flag, I prefer to write if(ugly) and if(!ugly) rather than if(ugly==1) and if(ugly==0). And as we're using EXIT_FAILURE we might as well use EXIT_SUCCESS too. Here is my first complete rewrite (keep on reading for my description of a slightly different approach to the problem):

/* Find "ugly numbers": their prime factors
   are all 2, 3 or 5 */
#include <stdio.h>
#include <stdlib.h>

const int MaxFactors = 20;
     /* Max number of prime factors to find */

enum UglyFactors {
  UglyFac1 = 2,
  UglyFac2 = 3,
  UglyFac3 = 5
};

/* Returns a zero-terminated array of number's
   prime factors, allocated on the heap */
static int *prime_factors(int number) {
  int *flist;
  int quotient = 0;
  int divisor = 2;
  int i = 0;

  flist = calloc(MaxFactors, sizeof(int));
  while(divisor <= number
        && i < MaxFactors - 1) {
    if(divisor == number) {
      flist[i] = quotient;
      break;
    }
    if(number % divisor == 0) {
      if(i == 0 || flist[i - 1] != divisor)
        flist[i++] = divisor;
      quotient = number / divisor;
      number = quotient;
    }
    else {
      ++divisor;
    }
  }
  return flist;
}

/* Returns 1 if number is ugly, otherwise 0 */
static int is_ugly(int number) {
  int i = 0;
  int ugly;
  int *flist = prime_factors(number);
  if(flist[0] == 0 && number != UglyFac1
     && number != UglyFac2
     && number != UglyFac3) {
    ugly = 0;
  }
  else {
    ugly = 1;
  }
  for(i = 0; flist[i]; ++i) {
    if(flist[i] != UglyFac1 &&
       flist[i] != UglyFac2 &&
       flist[i] != UglyFac3) {
      ugly = 0;
    }
  }
  free(flist);
  return ugly;
}

/* Range is given in arguments */
int main(int argc, char **argv) {
  int n;
  int first, last;
  if(argc !=3)
    exit(EXIT_FAILURE);
  first = atoi(argv[1]);
  last = atoi(argv[2]);
  for(n = first; n <= last; ++n) {
    if(is_ugly(n))
      printf("%d\n", n);
  }
  return EXIT_SUCCESS;
}

This solution works, but is not the most efficient. We don't actually need to list all the prime factors of ugly candidates, just check them until we find a factor that proves the number isn't ugly, again taking care not to pass primes as false positives. Therefore the prime_factors function can be deleted and is_ugly() replaced with the version below.

/* Returns 1 if a number is ugly, 0 if it
   isn't */
static int is_ugly(int number) {
  int divisor = 2;
  int ugly = 0;
  for(divisor = 2; divisor <= number;
      ++divisor) {
    if(number % divisor == 0) {
      if(divisor % UglyFac1 != 0 &&
         divisor % UglyFac2 != 0 &&
         divisor % UglyFac3 != 0) {
        ugly = 0;
        break;
      }
      else {
        ugly = 1;
      }
    }
  }
  return ugly;
}

Student Code Critique 28

Program 1

I'm newbie to C++ and I would like to know which would be the best (elegant and correct) solution for the following small (string) read problem with gets.

#include <iostream>
using namespace std;
#include <cstdio>

main() {
  int n;
  int waste; // needs this to work (read)
             // properly!!
  char name[51];
  cout << "Enter any integer number...\n";
  cin  >> n;
  cout << "Enter your name...\n";
  cin  >> waste; // 'gets' does not read the
                 // name without this line!!
  gets(name);
}

Mixing C and C++ functions doesn't seem the best way to write this program. Please provide alternatives, taking also into account its security implications.

Program 2

If I enter 23 and 5 the answer should be 23 * 5 = 115 my answer is off by five or whatever number 2 is. I was told I am not including the first two numbers in the loop. I thought when I cin the numbers it is including them. Does anyone have any suggestions on how I can fix this?

#include<iostream>
#include<iomanip>
#include<string>
using namespace std;
int main()
{
  while (1){
int   num1, num2, total = 0;
cout<<"Enter two numbers: ";
cin>>num1>>num2;
while (num1 != 1)
cout<<setw(5)<<num1<<setw(5)<<num2<<endl;
  num1 /= 2;
    num2 *= 2; 
  if (num1 % 2 != 0)
    total += num2;
}
cout<<"the total is  "<<total <<endl;
}  //End While 1
return 0;
}

There are numerous errors in both the code and the student's understanding. Please address these comprehensively.

From Paul F. Johnson

Program 1

If I ignore the obvious mistake of not having int before main(), there are a couple of problems with the code.

Firstly is the use of gets - it's a licence for things going wrong with undefined behaviour the most obvious to occur as a char array is being used to store the person's name. A far better solution would be for the name to be stored in a std::string variable which does not have a boundary (well, not in the same way as a char array does!). Buffer overflows account for more and bloodier problems with security than enough.

There is also a problem with the entry of the number - as it stands, it is possible for the user to enter just about anything they want as there are no forms of bounds or type checking.

Finally, we have the "dummy" use of another variable which isn't required (and again, can lead to problems without checking for the correct type). The stream can be cleared by use of cin.ignore.

By the simple application of cin.fail() and cin.getline(), the code can be transformed into something which (a) works and (b) is relatively secure.

A solution may be along the lines of

#include <iostream>
#include <string>
using namespace std;

int main() {
  int n;
  string name;
  cout << "Please enter a number : ";
  while(cin >> n, cin.fail()) {
    cout << "I need a number!" << endl;
    cin.clear();
    cin.ignore(numeric_limits<
               std::streamsize>::max(), '\n');
    // non compliant compilers may complain
    // here. If they do use cin.ignore();
  }
  cin.ignore(numeric_limit<
               std::streamsize>::max(), '\n');
  cout << "Please enter your name : ";
  cin.getline(name, '\n');
  cout << "Number : " << n << ", name : "
       << name << endl;
}

As newbie code goes, the original wasn't that bad. It was well laid out (which made debugging simpler) and contained many of the problems faced by a newbie where the most obvious answer is not always the correct one.

Program 2

First, the easy ones...

#include <string> is not required and somewhere, an open { is missing. This second problem would have been obvious if the student had bothered to try to compile the code!

I next come to the setw() calls - what are they there for? We are dealing with integers in num1, num2 and total, therefore using setw does seem to be a bit of a waste!

There doesn't seem to be a check on the entries either. There is nothing to stop the user entering # and @ for the numbers (or even CVu and Overload for that matter!).

The next couple of faults are in the logic. To start, we have the line

num1 /= 2;

If the user enters 0, the result value in num1 will be 1 which will throw the answer.

The line

if(num1 % 2 != 0)

really is bemusing! What is the point of it? Why does the program have to check if num1 is divisible by 2 with a 0 remainder? [The student is trying to implement a Russian peasant multiplication algorithm. The point is to discriminate whether num1 is even or not. David]

Finally, total only seems to be having num2 added to it (and even then, only as a result of a condition); num1 doesn't seem to be used anywhere other than as an entry. It is therefore completely possible for the user to be enter as many numbers as they like without the correct answer coming out ever.

Fixing the problem is simple - it requires being rewritten. Unfortunately, the student hasn't actually said what the question was, so it is a tad pointless trying to second guess what the original problem set was.

From Mark Easterbrook

Program 1

As suggested in the problem, mixing C and C++ I/O is not the best way to write this program. As you are learning C++, it is best to replace the Ctype parts with C++. This requires two changes:

Change "char name[51]" to "string name" (this will require an #include <string> to compile). It is nearly always better to use one of the C++ collections rather than an array. In this case a C++ string type will automagically expand to the size of the input name so you have one less thing to worry about.

Use C++ istream instead of gets(): cin << name. The waste variable and input is no longer required, nor is the #include <cstdio>.

It is worth pointing out a number of improvements that can be made to the code. main always returns an int so this needs to be specified: int main().

using namespace std will import the whole std namespace. It is often better to only import names you actually need, or to qualify every use.

Single character variable names are often frowned upon. Think how difficult it would be to search for all uses of the variable n in a large program!

At this point we have "fixed" the program and could let it rest, but it is worth looking at why gets() is bad, even in C code. gets() will read an unknown number of characters into a C string (char* or char[]) of predetermined length, therefore it is possible to read more characters than are allowed for. This is called "buffer overrun" and accounts for many of the security holes in software. The result of such a buffer overrun could be:

  • It writes over memory allocated but not used by the program. No amount of testing will show this up so the bug can remain hidden until something else changes: a different compiler, a different platform, or a simple code change somewhere else in the program.

  • It writes over memory not allocated to the program. If you are lucky your operating system will detect this and stop the program.

  • It overwrites memory used by the program. This causes the program to malfunction, and possibly later crash. This will be very difficult to track down.

  • It overwrites memory used by the program in such a way that the program does something completely different to that originally intended. If you are unlucky you are connected to the internet and this allows a stranger access to your computer!

As you cannot use gets(), what should you use instead? There are a number of options:

Use fgets() to read up to a line of input. It takes as parameters a pointer to the input buffer (like gets), a maximum number of characters (which prevents buffer overrun), and a file descriptor (e.g. stdin). It will stop reading either when the maximum number of characters are read, or at a new line character.

Use scanf() with the %s format string specifying a maximum length. For example, %50s will read a maximum of 50 characters (plus a terminator!).

Input characters one at a time using getchar() or getc().

You will need to read the documentation for each of these to see which meets your needs best. Each has different rules as to when it stops reading.

Program 2

Note: The program as presented will not compile because the inner loop has a closing brace but no opening brace. This can be corrected by adding the opening brace after the while expression. I have assumed this is just a printing error. [Actually the student provided the code as is. David]

I will take a guess that the problem set is to perform multiplication using binary arithmetic by adding in 2n x the second number for each bit set in the first. E.g. 23*5 is 10111 x 510 = 80+0+20+10+5.

The algorithm you have chosen is to shift the first number right bit by bit, testing the least significant (right-most) bit, while at the same time left-shifting the second number to obtain the powers of 2. This is almost correct, but the first value of num2 you are adding to the total has already been shifted, whereas it can be seen from the example above, the first addition should be the original entered value (5). This is what is meant by "…not including the first two numbers…". If you move the add-to-total to the beginning of the loop, then the total will be accumulated as 5+10+20…, which is what we want. We have now fixed the start-up conditions, so let's check the exit condition. We want to include all the bits from num1, so we want to continue the loop while there are still bits to process, in other words, while num1 is not all zero bits. The current test (num1!=1) does not do this, it will stop with the last bit still not processed. Let's change the condition to (num!=0) and give the program a test:

Enter two numbers: 23 5
   23    5
   11   10
    5   20
    2   40
    1   80
the total is 115

We now have a working program, but the code does not quite capture the intent: it is required to demonstrate how binary multiply can be performed by shift and add operations, but there are no shifts! Although we know that in C and C++ n/2 is equivalent to n>>1 and n*2 is equivalent to n<<1, using the bit shift operators would illustrate the intent more clearly. Similarly testing the least significant bit would be better as a mask and test rather than the remainder of a divide. Finally, the source code would benefit from some comments to explain what it is for.

Thus we end up with:

#include <iostream>
#include <iomanip>
#include <string>

using namespace std;

// Perform binary multiply of two integers
// using shift and add.  The total is built up
// by adding the matching power of two from
// the second number for each bit set in the
// first bit. Bits are tested in the first
// number by testing the LS bit and shifting
// right. The powers of two of the second
// number are generated by shifting left on
// each iteration.
// e.g. 23(10111)*5 = 80 + 0 + 20 + 10 + 5
// (summed in reverse order).

int main() {
  while(1) {
    int num1, num2, total=0;
    cout << "Enter two numbers: ";
    cin >> num1 >> num2;
    while(num1 != 0) {
      cout << setw(5) << num1
           << setw(5) << num2 << endl;
      if((num1&0x01) != 0)
        total += num2;
      num1 >>= 1;
      num2 <<= 1;
    }
    cout << "the total is " << total << endl;
  }
  return 0;
}

The Winner of SCC 28

The editor's choice is:

Tony Houghton

Please email to arrange for your prize.

Francis' Commentary

For my comments on the first little program for SCC28 see my Francis' Scribbles column elsewhere (working late and meeting deadlines resulted in my sending David some code I had already used in my column. Fortunately that does not matter too much.)

Here is my commentary on the second little program.

#include<iostream>
#include<iomanip>
#include<string>
using namespace std;
int main() {
  while(1) {
    int num1, num2, total = 0;
    cout << "Enter two numbers: ";
    cin >> num1 >> num2;
    while (num1 != 1) {
      cout << setw(5) << num1 
           << setw(5) << num2 << endl;
      num1 /= 2;
      num2 *= 2; 
      if((num1 % 2) != 0) total += num2;
    }
    cout << "the total is " << total << endl;
  }  // End While 1
  return 0;
}

First, in the above restatement of the program I have reformatted the code to make its structure more visible. Now let me focus on that structure before turning to the root of the problem.

Prepare For Exceptions

I strongly advocate that the default form of the definition of main() should encapsulate its code in a try block. That seems to me to be a good discipline for students as soon as they are conscious that errors may occur at runtime that result in an exception. Indeed they should be encouraged to validate such things as input and exceptions make it easy for them to have a default action when validation fails. So I would start with:

int main() {
  try {
    // main code
  }
  catch(...) {
    cerr << "An exception occurred.\n";
    return EXIT_FAILURE;
  }
  return EXIT_SUCCESS;
}

Now that is a fixed framework and it isn't even tedious to type because you just copy and paste it from a text file of standard bits of source code.

Repetition of Process

The first problem with the student's code is that it has no termination. He puts it all in a forever loop without any internal exit. I have no problem with forever loops (except that I use while(true)) as long as they have an internal exit (that is unless you are writing a process which must continue until the machine is switched off). In this case I would write something such as:

while(true) {
  // main process
  cout << "Do you want another? (y/n)";
  char yn;
  cin >> yn;
  if(yn == 'n' || yn == 'N') break;
}

Now we can argue about the details but the general idea should be considered as an idiom of programming (not just C++).

Appropriate Variable Names

As an ex-maths teacher I quickly recognised the algorithm being implemented by this program (goes under several names, try using Google to search for 'Russian Peasant Multiplication') but the variable names were not helpful. Let me suggest some others and polish up the code whilst I am at it.

cout << "Enter the pair of numbers you want "
     << "to multiply together: ";
int multiplicand(getint());
int multiplier(getint());
int product(0);

Now the variable names give a hint as to what is being done. Validation and handling bad input is all handled by the getint() function. Actually I would use my read() set of templates but that is just a convenience. However avoiding repetitious coding whilst always validating input should be learnt from the very beginning.

Getting the Right Test

Now we get to the actual reason the program does not work. Look at that inner while loop. Look at the test. Surely this is too early, or it is the wrong test because it fails immediately if the multiplicand is one. In other words multiplying one by anything will give a product of zero. There are several solutions but I would prefer:

while(multiplicand)

or if you do not like that form:

while(multiplicand != 0)

Getting the Right Order of Statements

For the algorithm (which is effectively using binary for multiplication) we add in the current value of the multiplier into the product if the current value of the multiplicand is odd (i.e. would end in one in binary), then the multiplicand is halved (shifted left) and the multiplier is doubled (shifted right). The order of these operations is vital and is the second point of error in the student's code. Leaving aside the display of the interim results, which I would have preferred to see include the interim value of the product in a third column) the meat of the algorithm is coded as:

if(multiplicand % 2) product += multiplier;
multiplicand /= 2;
multiplier *= 2;

Putting It All Together

Here is my complete program (well I have left out the front matter of headers and using directive):

int main() {
  try {
    while(true) {
      cout << "Enter the pair of numbers you "
           << "want to multiply together: ";
      int multiplicand(getint());
      int multiplier(getint());
      int product(0);
      while(multiplicand) {
        cout << setw(5) << multiplicand
             << setw(5) << multiplier
             << setw(10) << product
             << '\n';
        if(multiplicand % 2)
          product += multiplier;
        multiplicand /= 2;
        multiplier *= 2;
      }
      cout << "The product is " << product
           << ".\n\n";
      cout << "Do you want another? (y/n)";
      char yn;
      cin >> yn;
      if(yn == 'n' || yn == 'N') break;
    }
  }
  catch(...) {
    cerr << "An exception occurred.\n";}
    return EXIT_FAILURE;
  }
  return EXIT_SUCCESS;
}

Note that the depth of nesting for structures suggests that some refactoring into functions might be desirable. I would probably make the outermost loop a do-while one and use the return value from a function asking about repeating the process in the while. Something like:

do {
  // process
while(do_again());

I would also move out the display of the intermediate results to a function. I would also probably replace the computation block with a function call, but that is much more marginal because all the arguments have to be passed by reference and I doubt that we get much more clarity in exchange for using a function.

Now do feel free to comment on the coding style and anything else that you want to criticise. Remember that this column is supposed to be a kind of seminar and not a lecture.

Student Code Critique 29

(Submissions to by September 10th)

Looks like an ordinary snippet, doesn't it? Amazingly, it contains various mistakes for such a few lines. Please provide a correct version.

#include <iostream>
using std::cout;
using std::endl;

#include <list>
using std::list;

int main() {
  list<double>::iterator it;
    list<double> lst;
  *it = 34;
  *++it = 45;
    *++it = 87;
    it = lst.begin();
    for (;it < lst.end(); ++it){
      cout << it << '\t' << *it << endl;
  }
    system("pause");
  return 0;
}

Notes: 

More fields may be available via dynamicdata ..