2012-01-23 32 views
5

Tôi đang viết một số mã cần đọc fasta files, vì vậy một phần mã của tôi (bao gồm bên dưới) là trình phân tích cú pháp nhanh. Khi một chuỗi đơn có thể mở rộng nhiều dòng trong định dạng fasta, tôi cần phải nối nhiều dòng liên tiếp đọc từ tệp vào một chuỗi duy nhất. Tôi làm điều này, bằng cách realloc'ing chuỗi bộ đệm sau khi đọc tất cả các dòng, là chiều dài hiện tại của chuỗi cộng với độ dài của dòng đọc in Tôi làm một số công cụ khác, như tước không gian màu trắng vv Tất cả đều tốt cho chuỗi đầu tiên, nhưng các tệp fasta có thể chứa nhiều chuỗi. Vì vậy, tương tự, tôi có một mảng động của các cấu trúc với hai chuỗi (tiêu đề và chuỗi thực tế), là "char *". Một lần nữa, khi tôi gặp phải một tiêu đề mới (được giới thiệu bởi một dòng bắt đầu bằng '>'), tôi tăng số lượng các chuỗi, và thực hiện lại bộ đệm danh sách chuỗi. Các realloc segfaults trên phân bổ không gian cho chuỗi thứ hai vớiSử dụng realloc để mở rộng bộ đệm trong khi đọc từ tập tin treo

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

Đối với cuộc sống của tôi, tôi không thể thấy lý do tại sao. Tôi đã chạy nó thông qua gdb và mọi thứ dường như được làm việc (tức là tất cả mọi thứ được khởi tạo, các giá trị dường như lành mạnh) ... Dưới đây là các mã:

#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; 
} 
+0

Cảm ơn tất cả các nhận xét về thường trình chuỗi con. Tôi đã sửa nó trong mã của tôi. Tuy nhiên, tôi cũng nhận thấy rằng cách tôi xử lý các chỉ số tiêu cực là sai. Tôi nên thêm chỉ số âm, không trừ nó. Điều đó đang được nói, tôi cũng nhận ra rằng tôi đã sao chép hàm nền do lỗi, vì tôi không gọi nó trong phần còn lại của mã được dán. – sirlark

+0

'strip()' cũng bị lỗi. Nó sẽ làm những điều xấu với các chuỗi có độ dài bằng không. Có vẻ như bạn không gọi nó bằng các chuỗi như vậy, nhưng tôi nghĩ rằng nó sẽ là một điều tốt để sửa chữa khi nó được sử dụng ở nơi khác. –

Trả lời

1

Tôi nghĩ rằng sự cố tham nhũng bộ nhớ có thể là kết quả của cách bạn xử lý dữ liệu được sử dụng trong các cuộc gọi getline() của mình. Về cơ bản, line được phân bổ lại thông qua strndup() trong các cuộc gọi đến strip(), do đó kích thước bộ đệm đang được theo dõi trong linelen bởi getline() sẽ không còn chính xác nữa. getline() có thể vượt quá bộ đệm.

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 

} 
+0

Bạn có thể nhận được một số hàm đẹp, đơn giản, được thử nghiệm để cắt xâu tại chỗ tại đây: http://stackoverflow.com/a/2452438/12711 Sử dụng 'trim()' từ liên kết đó sẽ giải quyết vấn đề này (và ẩn khác (các) lỗi trong hàm 'strip()'). –

+0

Tôi đã khắc phục tất cả sự cố với dải (và đế) và vẫn gặp sự cố. Sự tương tác với getline và linelen là defintitely vấn đề. Cảm ơn vì sự giúp đỡ – sirlark

3

Một vấn đề tiềm năng là ở đây:

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

ret có thể không nhận được một terminator null. Xem 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. 

Ngoài ra còn có một lỗi ở đây:

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

gì nếu strlen(*s) là 0? Bạn sẽ đọc xong (*s)[-1].

Bạn cũng không đăng ký strip() rằng chuỗi không bao gồm toàn bộ không gian. Nếu có, bạn sẽ kết thúc với j < i.

chỉnh sửa: Chỉ cần nhận thấy rằng chức năng substr() của bạn không thực sự được gọi.

4

Bạn nên sử dụng chuỗi tìm kiếm một cái gì đó như thế này:

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

Điều này ngăn cản lỗi strncpy như những gì có vẻ như bạn đã thấy, và cho phép bạn làm strcat và bạn bè nhanh hơn.

Bạn cũng nên sử dụng mảng tăng gấp đôi cho mỗi chuỗi. Điều này ngăn cản quá nhiều phân bổ và memcpys. Một cái gì đó như thế này:

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; 
} 

Bây giờ tôi thấy bạn đang làm tin sinh học, có nghĩa là bạn có thể cần nhiều hiệu suất hơn tôi nghĩ. Bạn nên sử dụng chuỗi như thay vì điều này:

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

Bằng cách này, khi bạn phân bổ một đối tượng chuỗi, bạn gọi malloc(sizeof(struct string) + len) và tránh một cuộc gọi thứ hai để malloc. Đó là một công việc ít hơn một chút nhưng nó sẽ giúp đo lường, về tốc độ và cũng có thể phân mảnh bộ nhớ.

Cuối cùng, nếu điều này không thực sự là nguồn gốc của lỗi, có vẻ như bạn có một số tham nhũng. Valgrind sẽ giúp bạn phát hiện nó nếu gdb thất bại.

+0

@lief: Mức tiêu thụ bộ nhớ có nhiều vấn đề hơn tốc độ. Tôi không muốn phân bổ theo khối lượng gấp đôi và có không gian lãng phí. Phải thừa nhận rằng, đây không phải là vấn đề trong trình phân tích cú pháp fasta, nhiều hơn trong quá trình xử lý. – sirlark

+0

Do phân mảnh nội bộ malloc, bạn có thể sẽ sử dụng quá nhiều bộ nhớ ngay cả khi bạn không yêu cầu. Các mảng tăng gấp đôi khá đáng tin cậy, nhưng nếu chúng quá đáng sợ, hãy sử dụng một cái gì đó như 'malloc_usable_size' để đo phân mảnh của bạn. Nếu bạn chọn mảng tăng gấp đôi và đề xuất thứ hai của tôi về phân bổ độ dài và chuỗi bộ đệm với nhau, hãy cẩn thận rằng bạn bao gồm độ dài trong phép tính kích thước hoặc bạn có thể bị phân mảnh khủng khiếp (nếu bạn phân bổ 2^n + sizeof int , ví dụ). – leif

+0

'char ptr [0];' là không hợp lệ C. Bạn có nghĩa là 'char ptr [];', nhưng đây có thể là một tên xấu cho phần tử bây giờ vì nó là một mảng, không phải là một con trỏ. Tôi sẽ gọi nó là 'dữ liệu' hoặc' nội dung' hoặc một cái gì đó dọc theo những dòng đó. –

Các vấn đề liên quan