Continuing my K&R journey!

Please see below for my solution to:

Exercise 1-20: 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.

#include <stdio.h>

#define TAB         '\t'        //  character to look for
#define SPACE       '-'         //  tab replacement char
#define TABSIZE     8           //  fixed set of tab stops
#define MAXLINE     8000        //  maximum number of chars in a line

static int getLine(char line[]);

static void processLine(char line[], int lineCharCount);

int main() {
    int len;                //length of line
    char line[BUFSIZ];      //string constant array for appending ch

    while ((len = getLine(line)) > 0) {
        processLine(line, len);
    return 0;

static int getLine(char line[]) {
    int c;
    int i;

    for (i = 0; i < MAXLINE - 1 && (c = getchar()) != EOF && c != '\n'; ++i) {
        line[i] = c;
    if (c == '\n') {
        line[i] = c;
    line[i] = '\0';
    return i;

static void processLine(char line[], int lineCharCount) {
    int nonTabCount;            // running count of characters up until \t is encountered
    int i;                      // for loop
    nonTabCount = 0;

    for (i = 0; i < lineCharCount; i++) {
        if (line[i] == TAB) {
            line[i] = SPACE;
            while (TABSIZE - nonTabCount > 0) {
            nonTabCount = 0;
        } else {
            if (nonTabCount >= TABSIZE) {
                nonTabCount = 0;
  • \$\begingroup\$ How is a hyphen the same as a space? \$\endgroup\$
    – Reinderien
    Jan 18 at 2:03
  • 3
    \$\begingroup\$ Oh dear, I forgot to change back to space..... having hyphens was helpful while building the program. Gosh darnit. \$\endgroup\$ Jan 18 at 2:18

1 Answer 1


Overall: Aside from bug, a very good post.

Nice layout & formatting

Good object names

Bug: Inconsistent size

With char line[BUFSIZ]; and C definition of "The value of the macro BUFSIZ shall be at least 256.", calling getLine(line) is a problem. getLine() assumes the buffer is MAXLINE or 8000.

Better to use the same. Even better, pass the size into getLine().

// static int getLine(char line[]) {
static size_t getLine(char line[], size_t size) {

I like size first to allow static code analysis with:

static size_t getLine(size_t size, char line[size]) {

In this case, best to initialize to handle tiny sizes.

// int c;
int c = 0;

Pedantic: string size

String size can exceed INT_MAX. Better to use size_t to express lineCharCount to handle all strings.

// processLine(char line[], int lineCharCount)
processLine(char line[], size_t lineCharCount)

Then use size_t i; in the function.


Simpler and handles unsigned math well - no overflow.

// while (TABSIZE - nonTabCount > 0) {
while (TABSIZE > nonTabCount) {


I'd print the processed line afterwards and avoid printing in processLine(). Of course that is tricky as the line grows.

Why change line[]?

processLine() has line[i] = SPACE;, but why change only 1 space? If anything the de-tabify process might add multiple spaces and that complicates things. Code instead could putchar(SPACE); and leave line[] unchanged.

If processLine() changed to leave char line[] unchanged, consider making that paramter const char line[].


With processLine(char line[], int lineCharCount), I'd expect the function received a pointier to a string, in which case, the lineCharCount is not needed. Instead iterate until a null character is found.

Yet by passing lineCharCount does allow handling of a line of input with a null character that was read - an advance functionality.

Fixed tab size

"Assume a fixed set of tab stops, say every n columns." --> that sounds like n should be an option at program start to de-tabify at different values. Maybe that is for the next iteration of code.


Coding goal deserves to be in the code as a comment.

Advanced: long line

When getLine() fills the buffer, yet the line is still not done, consider how one would re-work the function to convey that incomplete read to the caller and if the remainder of the line should remain unread.

Declare and initialize in one step

// int nonTabCount;
// nonTabCount = 0;

int nonTabCount = 0;
  • \$\begingroup\$ No review on #define SPACE '-' as OP already id'ed that typo. \$\endgroup\$ Jan 18 at 3:51
  • \$\begingroup\$ iamericfletcher, Post lacks tests to show that code works. Next time, consider a test harness. \$\endgroup\$ Jan 18 at 4:10
  • \$\begingroup\$ I'm confused - in getLine in main(), what would be the size I'm passing into it? Would I declare size_t size; and then use that like so while ((len = getLine(size, line)) > 0)? \$\endgroup\$ Jan 18 at 11:37
  • \$\begingroup\$ Also, would I keep char line[BUFSIZ]; the way it is in main()? \$\endgroup\$ Jan 18 at 11:40
  • \$\begingroup\$ @iamericfletcher More like in main(), char line[BUFSIZ]; ... while ((len = getLine(sizeof line, line)) > 0). \$\endgroup\$ Jan 18 at 14:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.