Question

I'm looking at the second solution for the exercise:

http://users.powernet.co.uk/eton/kandr2/krx121.html

#include <stdio.h>

#define TABSTOP 4

int main(void)
{
    size_t spaces = 0;
    int ch;
    size_t x = 0;               /* position in the line */
    size_t tabstop = TABSTOP;   /* get this from the command-line 
                                 * if you want to */

    while ((ch = getchar()) != EOF)
    {
        if (ch == ' ')
        {
            spaces++;
        }
        else if (spaces == 0) /* no space, just printing */
        {
            putchar(ch);
            x++;
        }
        else if (spaces == 1) /* just one space, never print a tab */
        {
            putchar(' ');
            putchar(ch);
            x += 2;
            spaces = 0;
        }
        else
        {
            while (x / tabstop != (x + spaces) / tabstop) 
                /* are the spaces reaching behind the next tabstop ? */
            {
                putchar('\t');
                x++;
                spaces--;
                while (x % tabstop != 0)
                {
                    x++;
                    spaces--;
                }
            }

            while (spaces > 0) /* the remaining ones are real space */
            {
                putchar(' ');
                x++;
                spaces--;
            }
            putchar(ch); /* now print the non-space char */
            x++;
        }
        if (ch == '\n')
        {
            x = 0; /* reset line position */
        }
    }

    return 0;
}

I have two use cases:

  • 5 characters and then 2 spaces and then a character, and
  • 7 characters and then 2 spaces and then a character.

In use case 1, x is equal to 5 before encountering second space and in use case 2, x is equal to 7 before encountering second space. But in both instances, 2 subsequent spaces occur before the following block is executed:

while (x / tabstop != (x + spaces) / tabstop)

In the condition, notice you are dividing x by tabstop on both sides of the comparison. The only difference is that on the right hand side you increment x by spaces before performing the division. That's an important distinction. As long as spaces is equal to 0, then the two divisions will return the same value and the comparison will return true and the while will not execute.

However, if spaces is greater than 0, then there is a possibility that this comparison will return false. For example, if x is 7: (7 / 4 != (7 + 1) / 4), and in that case, the while loop will execute. But there is another variable. If x is not 7, but rather if it is 5, and if spaces is 2, then it will not execute, because 5/4 == 7/4, since both produce 1 (we drop the remainder).

How come encountering a second space after 5 regular characters yields a different outcome here than encountering a second space after 7 regular characters? I don't understand why in one situation it inserts a tab and the other situation it doesn't, when in both cases there are 2 consecutive spaces following the characters.

Was it helpful?

Solution

Your code is not the same as the example you reference.

The example you reference only replaces four spaces with a tab when four consecutive spaces occur.

You appear to want a program to replace the spaces between 'words' with a tab if the 'word' starts on or after a multiple of a tab stop.

Two different requirements.

You say that 5 characters, plus two spaces, plus one character should result in replacing the two spaces with a tab as the one character, the second 'word', starts on a tab stop.

At

while (x / tabstop != (x + spaces) / tabstop)

the value of x is 5, 5/4=1. The value of spaces is 2, (5+2)/4 = 1. Equal. No tab. Program is doing what it is programmed to do.

You want to replace the two spaces with a tab because the 8th character starts a word.

Your version of the example does not do that. x has the value 7.

You appear to want x to have the value 8. 5/4 = 1, 8/4 = 2, substitute a tab.

Change the program to include the current character in the count when you have two or more spaces and see what happens.

OTHER TIPS

I'll just post the code I wrote for that exercise awhile ago. Pass the "TABSTOP" on the command line, and if you don't it defaults to 4.

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#define BUFFER 1000

int main(int argc, char *argv[])
{
    int i = 0; 
    char c;
    int numspace = 0;
    int tablen;
    char string[BUFFER];

    tablen = !argv[1] ? 4 : atoi(argv[1]);

    for(i = 0; i < BUFFER; i++) string[i] = '\0';

    i = 0;

    for(i = 0; (c = getchar()) != EOF && i < BUFFER-1; i++) {
        if(c == ' ') {
            numspace++;
            if(numspace == tablen) {
                i -= numspace-1;
                c = '\t';
                numspace = 0;
            }
        } else if(!isblank(c)) {
            numspace = 0;
        }
        string[i] = c;
    }

    printf("\n%s\n", string);
    return 0;
}

The code in the question does not handle any tabs in the input, which is a debatable design decision. It never checks to see whether the current character is a tab or not. However, that is tangential to the question (though I regard it as a defect in the design of the program).

Given the input line:

abcde  fghi

when it enters the 'else' clause:

  • ch == 'f'
  • x == 5
  • spaces == 2
  • tabstop == 4

The first loop condition is:

while (x / tabstop != (x + spaces) / tabstop)

For the given values:

  • x / tabstop is 5 / 4 is 1
  • (x + spaces) / tabstop is (5 + 2) / 4 is 7 / 4 is 1

The condition is not true, so the loop does not execute; that is correct behaviour. The next loop outputs two spaces, and then f.

Now consider the input:

abcdefg  hijk

We get:

  • ch == 'h'
  • x == 7
  • spaces == 2
  • tabstop == 4

So, the loop condition is:

  • x / tabstop is 7 / 4 is 1
  • (x + spaces) / tabstop is (7 + 2) / 4 is 9 / 4 is 2

Therefore the code goes through the loop once, generating a tab character. The following code is:

x++;
spaces--;

This sets x to 8, spaces to 1.

while (x % tabstop != 0)
{
    x++;
    spaces--;
}

This loop is never executed because the condition is false. The next loop outputs a single space and then h. This is correct, and is different from the first case.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top