解析代码中的分段错误
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
将毫无意义。这尤其令人震惊,因为您实际上并不需要这个尺寸。
您可能不想使用 strncpy
。 strncpy
不会以 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);
}
}
}
这也不是最好的代码。但它显示了一些您可以考虑的事情。
我有这段代码可以提取两个指定字符串之间的字符串,特别是 <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
将毫无意义。这尤其令人震惊,因为您实际上并不需要这个尺寸。您可能不想使用
strncpy
。strncpy
不会以 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);
}
}
}
这也不是最好的代码。但它显示了一些您可以考虑的事情。