細かいreallocはフラグメンテーション化を助長する

C言語ポインタ完全制覇 4-2-1

realloc関数で必要な分をその都度確保していたらフラグメンテーション化の恐れがあるので、一般的にはreallocで一時変数を確保&拡張し使いまわすというのが正しいやり方のようです。

ということで前に実装した

ファイルの内容を行毎に配列して取得する - (void*)Pないと

を新しいやり方で実装してみます。(関数の仕様も一部変更します。)

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

#define ALLOC_SIZE 256

int readlines (FILE *fp,char ***line) {
    char *buf = NULL;
    char *p = NULL;
    int c;
    int buf_size = 0;
    int buf_index = 0;
    int line_size = 0;
    int line_index = 0;
    
    *line = NULL;
    
    while( (c = fgetc(fp)) != EOF ) {
        // 文字の終端用に一つ多めに確保しておく
        if ( buf_index+1 >= buf_size ) {
            buf = (char *)realloc(buf,sizeof(char)*(buf_size+=ALLOC_SIZE));
        }
        buf[buf_index++] = (char)c;
        if ( c == '\n' ) {
            buf[buf_index] = '\0';
            p = (char *)malloc(sizeof(char)*buf_index);
            if ( p == NULL ) return EOF;
            p = _strdup(buf);
            buf_index = 0;
            // 最後の行のために一つ多めに確保しておく
            if ( line_index+1 >= line_size ) {
                *line = (char **)realloc(*line,sizeof(char *)*(line_size+=ALLOC_SIZE));
            }
            (*line)[line_index++] = p;
        }
    }
    
    // 最後の行が存在してたら処理する
    if ( buf_index != 0 ) {
        buf[buf_index] = '\0';
        p = (char *)malloc(sizeof(char)*buf_index);
        if ( p == NULL ) return EOF;
        p = _strdup(buf);
        (*line)[line_index++]= p;
    }
    
    // 多く確保している分の切り詰めを行う
    if ( line_index < line_size ) {
        *line = (char **)realloc(*line,sizeof(char *)*line_index);
    }
    
    return line_index;
}

int main (void) {
    char **line = NULL;
    int i;
    int line_size;
    
    if ( (line_size = readlines(stdin,&line)) == EOF ) {
        puts("error!");
        return 1;
    }
    
    for(i=0;i<line_size;i++) {
        printf("%s",line[i]);
    }
    
    return 0;
}

できました。reallocで確保した値を一時変数として利用し、実際の値はmallocで確保しなおしているので極力フラグメンテーションにならないように頑張ってる感じです。ってでも実際に見えない部分の話なのでこれでうまくいってるのか今一わからなかったりしますが(笑)