解析代码中的分段错误

时间:2015-05-19 04:58:20

标签: c parsing segmentation-fault

我有这段代码,专门提取两个指定字符串<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);

}

1 个答案:

答案 0 :(得分:3)

这里有很多问题。

没有特别的顺序:

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

  • 在解析器中,您正在调用文件内容stat,而不是文件名。这通常会失败,而size将毫无意义。这是特别令人震惊的,因为你实际上并不需要这么大。

  • 您可能不想使用strncpy。除非在原始文件中找到NUL,否则strncpy不会终止副本。所以你最终会得到一个未终止的副本,这可能会造成各种各样的破坏。

    相反,只需使用memcpy(它也不会终止,但至少它不会让你误以为它可能)并自己添加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);
        }
    }
}

这也不是最好的代码。但它显示了一些你能想到的事情。