How can I effectively troubleshoot off-by-one bug in C?

79 views Asked by At

I've written a program to reverse a char array and also reverse words within that array. The program almost works as intended but I believe this is an off-by-one error. I've tried messing with the math involving the loop counters but have not been able to figure this one out. What tool or techniques can I use to solve this type of problem? I've tried printf statements and also using gdb and placing a watch on the counter variables.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void reverse_arr(char *arr, size_t len);
void print_chars(char *arr, size_t len);
void reverse_words(char *arr, size_t len);
int main(int argc, char **argv)
{
    char phrase[] = {'p','e','r','f','e','c','t',' ',
                    'm','a','k','e','s',' ','p','r',
                    'a','c','t','i','c','e'};

        size_t i;

    reverse_arr(phrase, sizeof(phrase));
    reverse_words(phrase,sizeof(phrase));
    print_chars(phrase, sizeof(phrase));

    return EXIT_SUCCESS;
}

void reverse_arr(char *arr, size_t len)
{
    size_t front, tail;
    tail = len-1;
    char tmp;
    for(front = 0; front < len/2; front++, tail--)
    {
        tmp = arr[front];
        arr[front] = arr[tail];
        arr[tail] = tmp;
    }

    return;
}

// 1. Search for a space
// 2. When space is found, that space is the place to stop and indicates all between the start and it are a word
// 3. Now call reverse_arr on the word and calculate the length of the word by subtracting tail - start
// 
void reverse_words(char *arr, size_t len)
{
    size_t tail, start;
    for(tail = start = 0; tail < len; tail++)
    {
        if(arr[tail] == ' ' || tail == len-1)
        {
            reverse_arr(&arr[start], tail - start);
            start = tail+1;
        }
    }
}

void print_chars(char *arr, size_t len)
{
    size_t i;
    for(i = 0; i < len; i++)
    {
        putchar(arr[i]);
    }
    putchar('\n');

    return;
}

This code returns practice makes erfectp. Clearly, this is an off-by-one bug but I've spent some time on this and suffered similar bugs in C in other programs.

1

There are 1 answers

2
Tom Karzes On

The bug is in reverse_words. Sometimes tail indexes the character after the last character of the word, and sometimes tail indexes the last character of the word itself.

The call to reverse_array from reverse_words is:

reverse_arr(&arr[start], tail - start);

This works if start indexes the first character of the word, and if there are tail - start characters in the word. So tail must index the character after the last character of the word.

The condition arr[tail] == ' ' is consistent with this, but end condition is not: (1) the loop exits too soon, and (2) the test tail == len-1 is also off by one.

This can be fixed by iterating one more time, and checking the end condition before trying to access arr[tail] (to avoid indexing past the end):

void reverse_words(char *arr, size_t len)
{
    size_t tail, start;
    for (tail = start = 0; tail <= len; tail++)
    {
        if (tail == len || arr[tail] == ' ')
        {
            reverse_arr(&arr[start], tail - start);
            start = tail+1;
        }
    }
}

Note that the loop exit condition is now <= rather then <, the in-loop end test has shifted by one, and the order of the checks within the loop is reversed.