31 Jan 1996

Outline

Today we're looking at miscellaneous examples of buggy code. If you study computer science long enough, you see, eventually you might get paid to intentionally write buggy code!

Some of these example will come from the homework, and some from the teaching staff's memory. The way to learn from this is to study the buggy code, try to find the errors, determine how to fix them, and see whether your answers match with ours.

Example list code

There are many errors in the following code. Examine it, trying to find all that you can.

 1   // Buggy Code
 2   #include 
 3   #include 
   
 4   typedef struct PERSON {
 5      char            name[20];
 6      char            grade;          // A,B,C,...
 7      int             age;
 8      PERSON          *next;
 9   } person_elt, *person_list;
   
10   typedef struct COURSE {
11      char            *course_name;
12      person_list     students;
13   } course_ele, *course_ptr;
   
14   // function prototypes
15   course_ptr read_course(void);
16   person_elt* read_student(void); 
17   void print_course(course_ptr);
18   void free_course(course_ptr);
   
   
19   // Read course name, then list of students (names, ages,
20   // grades), print out, and free storage
21   int
22   main(void) {
23      course_ptr      course = read_course();
24      print_course(course);
25      free_course(course);
26      return 0;
27   }

28   // BUGGY PORTION BEGINS HERE
   
29   // Read data for course from stdin.  First get course name,
30   // then get info for each student.  Stop on EOF
31   course_ptr
32   read_course(void) {
33      person_elt      next_student;
34      course_ptr      result;
35      result = new course_ptr;
36      cin >> result->course_name;
37      result->students = NULL;
   
38      while( next_student = read_student() != NULL ) {
39          result->students = next_student;
40          next_student->next = result->students->next;
41      }
42      return result;
43   }
   
44   // Read information about new person from stdin
45   person_list
46   read_student(void) {
47      person_list     result;
48      if((cin >> result->name, result->age, result->grade)
                == NULL)
49          return NULL;
50      else return result;
51   }
   
52   // Print course list. Course name followed by student list
53   void
54   print_course(course_ptr course) {
55      person_list stu;
56      cout << "Course: " << course.course_name << endl;
57      for (stu = course->students; stu; stu = stu->next)
58          cout << *stu->name << "Age: " << &stu->age
59              << "Grade: " << stu->grade << endl;
60   }
   
61   // Free all the memory allocated for a course
62   void
63   free_course(course_ptr course) {
64      delete course->course_name;
65      delete course->students;
66      delete course;
67   }

The corrected code follows:

 1   // Corrected Code
 2   #include 
 3   #include 
   
 4   typedef struct PERSON {
 5      char            name[20];
 6      char            grade;          // A,B,C,...
 7      int             age;
 8      PERSON          *next;
 9   } person_elt, *person_list;
   
10   typedef struct COURSE {
11      char            *course_name;
12      person_list     students;
13   } course_ele, *course_ptr;
   
14   // function prototypes
15   course_ptr read_course(void);
16   person_elt* read_student(void); 
17   void print_course(course_ptr);
18   void free_course(course_ptr);
   
   
19   // Read course name, then list of students (names, ages, grades), 
20   // print out, and free storage
21   int
22   main(void) {
23      course_ptr      course = read_course();
24      print_course(course);
25      free_course(course);
26      return 0;
27   }
   
28   // ALTERED PORTION BEGINS HERE
   
29   // Read data for course from stdin.  First get course name, then get
30   // info for each student.  Stop on EOF
31   course_ptr
32   read_course(void) {
33*     person_elt      *next_student;
34      course_ptr      result;
35a     result = new course_ele;
35b     result->course_name = new char[50];
36      cin >> result->course_name;
37      result->students = NULL;
   
38*     while((next_student = read_student()) != NULL ) {
39*         next_student->next = result->students;
40*         result->students = next_student;
41*     }
42      return result;
43   }
   
44   // Read information about new person from stdin
45   person_list
46   read_student(void) {
47*     person_list     result = new person_elt;
48*     if((cin >> result->name >> result->age >> result->grade) == NULL) {
49a         delete result;
49b         return NULL;
50a     } else {
50b         return result;
50c     }
51   }
   
52   // Print course list. Course name followed by student list
53   void
54   print_course(course_ptr course) {
55      person_list stu;
56*     cout << "Course: " << course->course_name << endl;
57      for (stu = course->students; stu; stu = stu->next)
58*         cout << stu->name<< " Age: " << stu->age << " Grade: "
59              << stu->grade << endl;
60   }
   
61   // Free all the memory allocated for a course
62   void
63   free_course(course_ptr course) {
64      delete course->course_name;
65a     person_list     temp;
65b     while(course->students) {
65c         temp = course->students;
65d         course->students = course->students->next;
65e         delete temp;
65f     }
66      delete course;
67   }

Homework bugs

We also had some interesting bugs in looking at students' homework attempts. The following were described by TA Doug Baker:

BUG 1

Description:  Using a while loop in a recursive call.
Behavior:  Infinite loop printing the last word in the list.
Misconception:  Not understanding that the recursion implements the loop.
Code:

void
print_in_reverse(list_t l) {
  while (l->next != NULL)
    print_in_reverse(l->next);
  cout << l->word << " ";
}

Correct (I hope!) Code:

void
print_in_reverse(list_t l) {
  if (l == NULL) return;
  print_in_reverse(l->next);
  cout << l->word << " ";
}

BUG 2

Description:  Misusing strings/pointers.  The bug was that a pointer was
a) being dereferenced when its value was NULL, or
b) being used as a scratch variable when it was pointing to data that
   shouldn't be destroyed (the data in the list).

Also, when the student was debugging this, the student modified
find_replacement to return a constant string instead of NULL (e.g. 'return
"not found"').  This seemed reasonable, and the student modified the test
for null to strcmp(replacement, "not found").  However, this masked the
real bug because the code didn't crash anymore, but rather modified the
memory used to store "not found".

It seemed like the created list doesn't contain what it was created
with.  All the patterns seemed to match the first pattern input by the
user.  When find_replacement returned NULL, we ended up with a
segfault (or bus error, I forget which).  However, when
find_replacement was returning "not found", what really happened was
that the first time find_replacement was called, it returned a pointer
to the constant string "not found".  This was then erroneously used as
the destination for the user's input replacement.  Thus, for example,
"not found" was overwritten with "Danny".  Then whenever
find_replacement was called again, it tried to return "not found", but
"not found" was now "Danny", which doesn't match (a different
instantiation of the constant string) "not found" which is what it was
being tested against.  Thus, "Danny" was used as the replacement for
every pattern.  (Surely the worst possible behavior for a Madlibs program!)


Behavior:  All patterns get replaced by first user input.
Misconception:  Assigning a semantics to a variable without paying
                attention to what is really happening.  Not understanding
                how constant strings are treated internally.
Code:

int main(void){
...
  char *replacement;
  word_t new_word;
...
  while(the_text_file >> new_word) {
    if (new_word[0] != '*') {
      lis = insert_word(lis, new_word);
    } else {
      replacement = find_replacement(pl, new_word);
      if (replacement != NULL) {
        lis = insert_word(lis, replacement);
      } else {
        cout << "Enter a " << (new_word + 1) << ":" << endl;
        cin >> replacement;
        lis = insert_word(lis, replacement);
        pl = insert_word_pair(pl, new_word, replacement);
      }
    }
  }
...