# K&R Exercise 1-20: tabs to spaces

I'm learning C with K&R 2nd Ed. I've just completed the exercise 1-20 and I would know if my code is correct (i.e. answering the question) and if my style is not too bad. Or just some feebacks to improve myself!

K&R Exercise 1-20 p.34

Write a program detab that replaces tabs in the input with the proper number * of blanks to space to the next tab stop. Assume a fixed set of tab stops, say every n columns. Should n be a variable or a symbolic parameter?

I put '-' because it's more readable than ' '. But you can totally change it.

#include <stdio.h>
#define COLUMN 8

int main()
{
int c, i, nc;
nc = 0;

while ((c = getchar()) != EOF)
{
if (c != '\t' && c != '\n')
nc = (nc + 1) % COLUMN;

if (c == '\n')
nc = 0;

if (c == '\t') {
for (i=1; i<=(COLUMN - nc); ++i) {
putchar('-');
}
nc = 0;
}
else
putchar(c);
}

return(0);
}

• I think K&R style would put the { on the same line as the while. Also, K&R doesn't put parentheses around a return value. And return should have four spaces in front of it, like the rest of your main function.
– S. Morgenstern
Feb 12 '15 at 22:59
• AFAIR they dropped parantheses for return statement in second edition. There is really no need for them.
– Grzegorz Szpetkowski
Feb 12 '15 at 23:02
• K&R would also do i = 1; i <= (..., rather than without spaces like you wrote it. All of this only applies if you want to write K&R style, rather than something else.
– S. Morgenstern
Feb 12 '15 at 23:05

Here is an alternate algorithm:

while ((c = getchar()) != EOF) {
switch (c) {/* switch instead of if-else... */
case '\n':
nc = 0;
putchar(c);
break;
case '\t':
for (i = COLUMN - nc % COLUMN, nc += i; i > 0; --i) {
putchar('-');
}
break;
default:
++nc; /* increment is faster than increment plus divide */
putchar(c);
break;
}
}


nc keeps track of the current column position within the line.

• this line: 'for (i = COLUMN - nc % COLUMN, nc += i; i > 0; --i) {' might be a problem, as there is no guarantee in C on the order of evaluation and this line uses a side effect, the result of nc could windup being incorrect. It is not a good idea to depend on presidence when performing a calculation (use parens) Feb 12 '15 at 23:40
• @Mahé when the loop starts, i is assigned the number of spaced to add. And because the same value needs to be added to nc, it is done right there (after comma). Setting nc to 0 would also work, but I've decided not to break semantics when nc is a position inside the line, not tab offset counter.
– Valeri Atamaniouk
Feb 12 '15 at 23:42
• @user3629249 I believe you confuse comma operator and function parameter list evaluation.
– Valeri Atamaniouk
Feb 12 '15 at 23:46
• @ValeriAtamaniouk ok thanks, sorry I delete my question because I thought it was stupid, but visibly not, your answer help me!
– Mahé
Feb 12 '15 at 23:47
• @ValeriAtamaniouk Indeed the if-else is not here very practice because I have to write a third case like : else if (c != '\t' && c != '\n'). But I should not know the "switch" structure yet! :) Thanks for your feedback and for (i = COLUMN - nc % COLUMN, nc += i; i > 0; --i) line
– Mahé
Feb 13 '15 at 0:20