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.
There are many errors in the following code. Examine it, trying to find all that you can.
1 // Buggy Code 2 #include3 #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 #include3 #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 }
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);
}
}
}
...