解析代码中的分段错误

Segmentation fault in parsing code

我有这段代码可以提取两个指定字符串之间的字符串,特别是 <title></title>。但是当我 运行 程序时,我得到 Segmentation Fault: 11 任何解决方案?

int main(){

        struct stat st;
        stat("test.txt", &st);
        int size = st.st_size;
        printf("%d\n", size);
        FILE *f = fopen("test.txt", "rb");

        char *bytes = (char*)malloc(size);        
        fread(bytes,size,1,f);
        fclose(f);
        parser(bytes);
        return 0;
}

void parser(char *bytes){
        struct stat st;
        stat(bytes, &st);
        int size = st.st_size;
        char *output = (char*)malloc(size);
        char *ptr = strstr(bytes, "<title>");
        char *ptr2 = strstr(ptr, "</title>");
        if(ptr2){
            strncpy(output, ptr+7, (ptr2 - (ptr+7)));
            puts(output);
            free(output);
            parser(ptr2);
        }
        free(bytes);
        free(output);

}

这里问题比较多

排名不分先后:

  • 你正在双重释放 output(在你找到字符串的情况下)。这很可能会引发段错误,尽管不一定是在第二次调用 free 时。即使 bytes 不是从 malloc 返回的地址(在对解析的递归调用中),您也会释放它。这也会导致问题,这是糟糕的设计:函数通常不应释放作为参数传递给它们的字符串。

  • 在解析器中,您是针对文件内容而不是文件名调用 stat。这通常会失败,并且 size 将毫无意义。这尤其令人震惊,因为您实际上并不需要这个尺寸。

  • 您可能不想使用 strncpystrncpy 不会以 null 终止副本,除非在原件中发现 NUL。所以你最终得到一个未终止的副本,这可能会导致各种破坏。

    相反,只需使用 memcpy(它也不会以 null 终止,但至少不会让您误以为它可能)并自己添加 NUL。

  • 您最初从文件中读取的字符串不是以 NUL 结尾的。所以 strstr 将继续读取超出字符串末尾的内容。

  • parser 是递归的而不是尾递归的。它可以很容易地编写为尾递归,并且您的 C 编译器可能能够在这种情况下应用 TCO,但按照编写的那样,它有可能建立一个大的调用堆栈。

  • parser 在对结果调用 strstr 之前不验证第一个 strstr 是否找到了字符串。因此,当没有更多 <title> 可查找时,您将调用 ptr2 = strstr(NULL, "</title>");。那肯定会出现段错误。

下面是一些可能有用的代码:

/* Forward declare parser */
void parser(char *bytes);

int main(){
        struct stat st;
        stat("test.txt", &st);
        /* CHECK RETURN VALUE */
        int size = st.st_size;
        printf("%d\n", size);
        FILE *f = fopen("test.txt", "rb");
        /* CHANGE: need space for the NUL */
        char *bytes = malloc(size + 1);        
        size_t nread = fread(bytes,size,1,f);
        if (nread != size) { /* HANDLE ERROR */ }
        /* CHANGE: NUL terminate string */
        bytes[nread] = 0;
        fclose(f);
        parser(bytes);
        /* CHANGE: We allocated bytes, we free it */
        free(bytes);
        return 0;
}

void parser(char *bytes){
    char *ptr = strstr(bytes, "<title>");
    /* CHANGE: Make sure strstr found something */
    if (ptr) {
        /* Skip over the found string */
        ptr += 7:
        char *ptr2 = strstr(ptr, "</title>");
        if (ptr2) {
            /* Don't allocate buffer until we need it */
            /* Remember to leave space for the NUL */
            char* output = malloc(ptr2 - ptr + 1);
            memcpy(output, ptr, ptr2 - ptr);
            /* null-terminate */
            output[ptr2 - ptr] = 0;
            puts(output);
            free(output);
            parser(ptr2);
        }
    }
}

这也不是最好的代码。但它显示了一些您可以考虑的事情。