5

fastaファイルを読み取る必要のあるコードを書いています、したがって、私のコードの一部(以下に含まれています)はfastaパーサーです。単一のシーケンスはfasta形式で複数の行にまたがることができるため、ファイルから読み取られた複数の連続する行を1つの文字列に連結する必要があります。これを行うには、すべての行を読み取った後に文字列バッファを再割り当てして、シーケンスの現在の長さに読み込んだ行の長さを加えたものにします。空白の削除など、他の処理も行います。最初のシーケンスですが、fastaファイルには複数のシーケンスを含めることができます。同様に、「char *」である2つの文字列(タイトルと実際のシーケンス)を持つ構造体の動的配列があります。繰り返しますが、新しいタイトル('>'で始まる行で紹介されます)に遭遇すると、シーケンスの数を増やし、シーケンスリストバッファーを再割り当てします。2番目のシーケンスにスペースを割り当てる際のreallocsegfaults

*** glibc detected *** ./stackoverflow: malloc(): memory corruption: 0x09fd9210 ***
Aborted

私の一生の間、私には理由がわかりません。私はそれをgdbで実行しましたが、すべてが機能しているようです(つまり、すべてが初期化され、値は正常に見えます)...コードは次のとおりです。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
#include <errno.h>

//a struture to keep a record of sequences read in from file, and their titles
typedef struct {
    char *title;
    char *sequence;
} sequence_rec;

//string convenience functions

//checks whether a string consists entirely of white space
int empty(const char *s) {
    int i;
    i = 0;
    while (s[i] != 0) {
        if (!isspace(s[i])) return 0;
        i++;
    }
    return 1;
}

//substr allocates and returns a new string which is a substring of s from i to
//j exclusive, where i < j; If i or j are negative they refer to distance from
//the end of the s
char *substr(const char *s, int i, int j) {
    char *ret;
    if (i < 0) i = strlen(s)-i;
    if (j < 0) j = strlen(s)-j;
    ret = malloc(j-i+1);
    strncpy(ret,s,j-i);
    return ret;
}

//strips white space from either end of the string
void strip(char **s) {
    int i, j, len;
    char *tmp = *s;
    len = strlen(*s);
    i = 0;
    while ((isspace(*(*s+i)))&&(i < len)) {
        i++;
    }
    j = strlen(*s)-1;
    while ((isspace(*(*s+j)))&&(j > 0)) {
        j--;
    }
    *s = strndup(*s+i, j-i);
    free(tmp);
}


int main(int argc, char**argv) {
    sequence_rec *sequences = NULL;
    FILE *f = NULL;
    char *line = NULL;
    size_t linelen;
    int rcount;
    int numsequences = 0;

    f = fopen(argv[1], "r");
    if (f == NULL) {
        fprintf(stderr, "Error opening %s: %s\n", argv[1], strerror(errno));
        return EXIT_FAILURE;
    }
    rcount = getline(&line, &linelen, f);
    while (rcount != -1) {
        while (empty(line)) rcount = getline(&line, &linelen, f);
        if (line[0] != '>') {
            fprintf(stderr,"Sequence input not in valid fasta format\n");
            return EXIT_FAILURE;
        }

        numsequences++;
        sequences = realloc(sequences,sizeof(sequence_rec)*numsequences);
        sequences[numsequences-1].title = strdup(line+1); strip(&sequences[numsequences-1].title);
        rcount = getline(&line, &linelen, f);
        sequences[numsequences-1].sequence = malloc(1); sequences[numsequences-1].sequence[0] = 0;
        while ((!empty(line))&&(line[0] != '>')) {
            strip(&line);
            sequences[numsequences-1].sequence = realloc(sequences[numsequences-1].sequence, strlen(sequences[numsequences-1].sequence)+strlen(line)+1);
            strcat(sequences[numsequences-1].sequence,line);
            rcount = getline(&line, &linelen, f);
        }
    }
    return EXIT_SUCCESS;
}
4

3 に答える 3

4

次のような文字列を使用する必要があります。

struct string {
    int len;
    char *ptr;
};

これにより、見たように見えるようなstrncpyのバグを防ぎ、strcatや友達をより速く実行できるようになります。

また、文字列ごとに倍増配列を使用する必要があります。これにより、割り当てとmemcpyが多すぎるのを防ぎます。このようなもの:

int sstrcat(struct string *a, struct string *b)
{
    int len = a->len + b->len;
    int alen = a->len;
    if (a->len < len) {
        while (a->len < len) {
            a->len *= 2;
        }
        a->ptr = realloc(a->ptr, a->len);
        if (a->ptr == NULL) {
            return ENOMEM;
        }
    }
    memcpy(&a->ptr[alen], b->ptr, b->len);
    return 0;
}

あなたがバイオインフォマティクスをやっているのを見ました。つまり、おそらく私が思っていたよりも多くのパフォーマンスが必要だということです。代わりに、次のような文字列を使用する必要があります。

struct string {
    int len;
    char ptr[0];
};

このように、文字列オブジェクトを割り当てるときに、malloc(sizeof(struct string) + len)mallocへの2回目の呼び出しを呼び出して回避します。これはもう少し手間がかかりますが、速度とメモリの断片化の点で、かなり役立つはずです。

最後に、これが実際にエラーの原因ではない場合は、破損しているように見えます。Valgrindは、gdbが失敗した場合にそれを検出するのに役立つはずです。

于 2012-01-23T14:46:50.437 に答える
3

1つの潜在的な問題はここにあります:

strncpy(ret,s,j-i);
return ret;

retnullターミネータを取得しない可能性があります。参照してくださいman strncpy

       char *strncpy(char *dest, const char *src, size_t n);

       ...

       The strncpy() function is similar, except that at most n bytes  of  src
       are  copied.  Warning: If there is no null byte among the first n bytes
       of src, the string placed in dest will not be null terminated.

ここにもバグがあります:

j = strlen(*s)-1;
while ((isspace(*(*s+j)))&&(j > 0)) {

strlen(*s)0の場合はどうなりますか?あなたは読むことになります(*s)[-1]

strip()また、文字列が完全にスペースで構成されていないこともチェックインしません。もしそうなら、あなたはで終わるでしょうj < i

編集:substr()関数が実際に呼び出されないことに気づきました。

于 2012-01-23T14:33:27.840 に答える
1

メモリ破損の問題は、呼び出しで使用されるデータの処理方法の結果である可能性があると思いますgetline()。基本的に、はへの呼び出しでをline介して再割り当てされるため、によって追跡されるバッファサイズは正確ではなくなります。バッファをオーバーランする可能性があります。strndup()strip()linelengetline()getline()

while ((!empty(line))&&(line[0] != '>')) {

    strip(&line);    // <-- assigns a `strndup()` allocation to `line`

    sequences[numsequences-1].sequence = realloc(sequences[numsequences-1].sequence, strlen(sequences[numsequences-1].sequence)+strlen(line)+1);
    strcat(sequences[numsequences-1].sequence,line);

    rcount = getline(&line, &linelen, f);   // <-- the buffer `line` points to might be
                                            //      smaller than `linelen` bytes

}
于 2012-01-23T18:02:45.067 に答える