The programming language C (K & R) ex1-20. I encounter problems

advertisements
/*
 * 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.
 * Should n be a variable or a symbolic parameter?
 *
*/

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

#define  N 4

void detab(char **str);

int main(){
    char *line=NULL;
    char *newline;
    int len;
    while(getline(&line,&len,stdin)!=-1){
        detab(&line);
        printf("%s",line);
    }
    free(line);
    return 0;
}
void detab(char **str){
    int len=0,num=0;
    int i=0;
    char c;
    while((c=(*str)[i])!='\0'){
        if(c=='\t'){   // get the number of tab
            num++;
        }
        len++;          // get length of string
        i++;
    }
    char *newline;
    newline=(char *)malloc(len+(N-1)*num+1);   //use four blank replace one tab
    if(newline==NULL){
        fprintf(stderr,"can't malloc space\n");
    }
    i=0;
    int j=0;        //index of newline
    while((c=(*str)[i])!='\0'){
        if(c=='\t'){
            int k;
            for(k=0;k<N;k++){
                newline[j]=' ';
                ++j;
            }
        }
        else{
            newline[j]=c;
            ++j;
        }
        ++i;
    }
    newline[j]='\0';
    free(*str);
    *str=newline;
}

When I enter a short string, it works correctly, but if I enter a long string of maybe 50 characters, it says this:

*** Error in `./a.out': free(): invalid next size (fast): 0x0961b068 ***
Aborted (core dumped)

I have been stuck here for almost three hours. Please help me.

It works OK, if I try to use single pointer, like this:

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

#define  N 4

char* detab(char *str);

int main(){
    char *line=NULL;
    char *newline;
    int len;
    while(getline(&line,&len,stdin)!=-1){
        newline = detab(line);
        printf("%s",newline);
        free(newline);
    }
    free(line);
    return 0;
}
char* detab(char *str){
    int len=0,num=0;
    int i=0;
    char c;
    while((c=str[i])!='\0'){
        if(c=='\t'){
            num++;
        }
        len++;
        i++;
    }
    char *newline;
    newline=(char *)malloc(len+(N-1)*num+1);   //use four blank replace one tab
    if(newline==NULL){
        fprintf(stderr,"can't malloc space\n");
    }
    i=0;
    int j=0;        //index of newline
    while((c=str[i])!='\0'){
        if(c=='\t'){
            int k;
            for(k=0;k<N;k++){
                newline[j]=' ';
                ++j;
            }
        }
        else{
            newline[j]=str[i];
            ++j;
        }
        ++i;
    }
    newline[j]='\0';
    return newline;
}


I solved problem 1_20 by reading each input line into a doubly linked list. Each node in the list represents one character of the line in sequence.

After creating the linked list, I delete white space from the end of the list, i.e. from the end of the line.

Then I walk the linked list, scanning for tabs, keeping a column count as I go.

When I encounter a tab, I mark its column number and calculate how many spaces I need until the next tabstop. The formula is:

tabstop = ((col + (m-1)) / m) * m;

where:

  • tabstop is the next tabstop
  • m is the distance between tabstops
  • col is the column where the tab occurs

I replace the tab with a space and insert new nodes of single spaces in the linked list until I reach the next tabstop.

I continue walking the linked list from the column of the tabstop, searching for the next tab character and repeat the conversion process.

Once I reach the end of the linked list, I print it out as the output line.

Using a doubly linked list may seem tedious, but it greatly simplifies the logic of the main() function in the program.

The main() function says:

while (not end of file)
    {
    getline()
    remove_final_white_space()
    convert_tabs_to_spaces()
    putline()
    }

The getline() function creates the linked list with tab characters.

The putline() function flushes the linked list while it prints one character at a time.