使用线程计算文件中单词出现次数的程序中的分段错误
Segmentation fault error in a program for counting no of occurences of a word in a file using threads
所以我遇到了以下问题:实现一个程序,该程序将文件名后跟单词作为参数。对于每个单词,创建一个单独的线程,计算它在给定 file.Print 中出现的所有单词的总和。
我的代码是:
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <pthread.h>
pthread_mutex_t mtx; // used by each of the three threads to prevent other threads from accessing global_sum during their additions
int global_sum = 0;
typedef struct{
char* word;
char* filename;
}MyStruct;
void *count(void*str)
{
MyStruct *struc;
struc = (MyStruct*)str;
const char *myfile = struc->filename;
FILE *f;
int count=0, j;
char buf[50], read[100];
// myfile[strlen(myfile)-1]='[=10=]';
if(!(f=fopen(myfile,"rt"))){
printf("Wrong file name");
}
else
printf("File opened successfully\n");
for(j=0; fgets(read, 10, f)!=NULL; j++){
if (strcmp(read[j],struc->word)==0)
count++;
}
printf("the no of words is: %d \n",count);
pthread_mutex_lock(&mtx); // lock the mutex, to prevent other threads from accessing global_sum
global_sum += count; // add thread's count result to global_sum
pthread_mutex_unlock(&mtx); // unlock the mutex, to allow other threads to access the variable
}
int main(int argc, char* argv[]) {
int i;
MyStruct str;
pthread_mutex_init(&mtx, NULL); // initialize mutex
pthread_t threads[argc-1]; // declare threads array
for (i=0;i<argc-2;i++){
str.filename = argv[1];
str.word = argv[i+2];
pthread_create(&threads[i], NULL, count, &str);
}
for (i = 0; i < argc-1; ++i)
pthread_join(threads[i], NULL);
printf("The global sum is %d.\n", global_sum); // print global sum
pthread_mutex_destroy(&mtx); // destroy the mutex
return 0;
}
当我尝试 运行 它时,我得到了分段错误。这是为什么?
谢谢!
在 main()
中,您的两个 i
循环不同
for (i=0;i<argc-2;i++){
...
pthread_create(&threads[i], NULL, count, &str);
}
然后
for (i = 0; i < argc-1; ++i)
pthread_join(threads[i], NULL);
并且在第二个循环中,您引用的 threads[argc-2]
不是在第一个循环中创建的。
一次(可能)阅读(最多)10 个字符会漏掉正在搜索的单词的某些实例。
strcmp() 始终从 10 个字符的开头开始。
1) 需要在文件的任意位置查找目标词。
2) 需要在读入缓冲区的任意位置寻找目标词。
建议:
0) clear input buffer
1) input one char at a time,
accumulating characters in the input buffer,
2) when a word separator found, (for instance a space or EOF)
3) then check if the word matches the target word.
4) if matches, increment count.
5) if EOF, then exit, else goto 0
首先,您的代码格式非常糟糕。它甚至不一致。它也没有显示您正在编译启用警告。
如果你是大学课程,他们没有告诉你如何格式化代码和编译时出现警告,我强烈建议你问问你的导师。
如果使用 gcc,请添加 -Wall -Wextra。对于编码风格,我建议从 Linux or FreeBSD 中窃取一个。有各种编辑器可以为您格式化代码,包括像 vim 这样的真正的编辑器(值得一试,尽管它看起来很苛刻)。
您的编码风格可以帮助您克服困难。
void *count(void*str)
{
MyStruct *struc;
struc = (MyStruct*)str;
const char *myfile = struc->filename;
FILE *f;
int count=0, j;
char buf[50], read[100];
buf 未使用,如果您启用了警告,您将了解到这一点。 read 是个坏名字。
// myfile[strlen(myfile)-1]='[=11=]';
if(!(f=fopen(myfile,"rt"))){
printf("Wrong file name");
}
else
因为您没有 return(您应该有),所以您正在为执行不应该执行的代码而搞砸。猜不到,您的 'else' 子句无效。您缺少大括号,因此即使文件打开操作失败,也会执行下面的 for 循环。
printf("File opened successfully\n");
for(j=0; fgets(read, 10, f)!=NULL; j++){
10?似乎是打字错误,因为您可能是说 100。如果您使用 sizeof,就不会发生这种情况。
if (strcmp(read[j],struc->word)==0)
count++;
}
不清楚你在这里做什么。似乎您想从 read[0]、read1 等开始执行 strcmp。但是你 读取 替换原始缓冲区中的内容的新数据,然后 然后 你将它向前推进一个。这毫无意义。最后,无论如何你做错了。 read[j] 不计算地址,如果您要求,编译器会再一次告诉您。
strcmp 方法无论如何都非常糟糕。尝试一种尝试匹配第一个字符并从那里开始工作的方法。
int main(int argc, char* argv[]) {
标准错位“*”。请改用 char *argv[]。
int i;
MyStruct str;
pthread_mutex_init(&mtx, NULL); // initialize mutex
pthread_t threads[argc-1]; // declare threads array
强烈不推荐。首先验证参数,然后有一个专用变量来保存一定数量的线程。此时就可以分配一个数组了。
for (i=0;i<argc-2;i++){
str.filename = argv[1];
str.word = argv[i+2];
pthread_create(&threads[i], NULL, count, &str);
}
与线程类似,将路径保存在某处。将其称为 argv1 是一种糟糕的风格,它会反过来咬你一口。单词使用 argv 就可以了。
然而,这通常是错误的。您设置一个本地结构并将其传递给一个线程,然后立即更改它。所以发生的事情是在一天结束时你所有的线程都在计算同一个词。但是他们数的单词一直在变化。
for (i = 0; i < argc-1; ++i)
pthread_join(threads[i], NULL);
去图吧。您没有保存一定数量线程的变量,这导致了这种不一致(argc - 1 与 argc - 2)。
一般来说,这个问题在很大程度上可以通过正确阅读编译器警告来解决,如果采用基本的良好做法,通常可以避免。
当然,无论如何都会发生错误,在这种情况下,您至少可以缩小范围。
最后,关于一般方法的几句话。目前还不清楚这次演习的目的是什么。你实际上必须强迫自己使用 pthread_create 和 pthread_join 以外的任何东西。假设唯一的要求是使用线程。
我不知道他们是强制您多次打开文件还是什么。多次打开和阅读内容不仅浪费,而且会导致文件被替换并且某些线程打开另一个文件的情况。
一个 OK 的解决方案是在 main 中打开文件一次。打开后,您将映射文件和 fstat 以获取大小。如果由于某种原因你不能使用 mmap,你会 malloc 一个足够大的缓冲区并读取文件。
然后所有线程都可以获得该缓冲区的地址、要查找的字以及它们应将计数器存储到的地址(每个线程获得不同的地址)。
当所有线程都退出时,您循环对结果求和。
两种方式都不涉及锁定。
所以我遇到了以下问题:实现一个程序,该程序将文件名后跟单词作为参数。对于每个单词,创建一个单独的线程,计算它在给定 file.Print 中出现的所有单词的总和。
我的代码是:
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <pthread.h>
pthread_mutex_t mtx; // used by each of the three threads to prevent other threads from accessing global_sum during their additions
int global_sum = 0;
typedef struct{
char* word;
char* filename;
}MyStruct;
void *count(void*str)
{
MyStruct *struc;
struc = (MyStruct*)str;
const char *myfile = struc->filename;
FILE *f;
int count=0, j;
char buf[50], read[100];
// myfile[strlen(myfile)-1]='[=10=]';
if(!(f=fopen(myfile,"rt"))){
printf("Wrong file name");
}
else
printf("File opened successfully\n");
for(j=0; fgets(read, 10, f)!=NULL; j++){
if (strcmp(read[j],struc->word)==0)
count++;
}
printf("the no of words is: %d \n",count);
pthread_mutex_lock(&mtx); // lock the mutex, to prevent other threads from accessing global_sum
global_sum += count; // add thread's count result to global_sum
pthread_mutex_unlock(&mtx); // unlock the mutex, to allow other threads to access the variable
}
int main(int argc, char* argv[]) {
int i;
MyStruct str;
pthread_mutex_init(&mtx, NULL); // initialize mutex
pthread_t threads[argc-1]; // declare threads array
for (i=0;i<argc-2;i++){
str.filename = argv[1];
str.word = argv[i+2];
pthread_create(&threads[i], NULL, count, &str);
}
for (i = 0; i < argc-1; ++i)
pthread_join(threads[i], NULL);
printf("The global sum is %d.\n", global_sum); // print global sum
pthread_mutex_destroy(&mtx); // destroy the mutex
return 0;
}
当我尝试 运行 它时,我得到了分段错误。这是为什么? 谢谢!
在 main()
中,您的两个 i
循环不同
for (i=0;i<argc-2;i++){
...
pthread_create(&threads[i], NULL, count, &str);
}
然后
for (i = 0; i < argc-1; ++i)
pthread_join(threads[i], NULL);
并且在第二个循环中,您引用的 threads[argc-2]
不是在第一个循环中创建的。
一次(可能)阅读(最多)10 个字符会漏掉正在搜索的单词的某些实例。
strcmp() 始终从 10 个字符的开头开始。
1) 需要在文件的任意位置查找目标词。
2) 需要在读入缓冲区的任意位置寻找目标词。
建议:
0) clear input buffer
1) input one char at a time,
accumulating characters in the input buffer,
2) when a word separator found, (for instance a space or EOF)
3) then check if the word matches the target word.
4) if matches, increment count.
5) if EOF, then exit, else goto 0
首先,您的代码格式非常糟糕。它甚至不一致。它也没有显示您正在编译启用警告。
如果你是大学课程,他们没有告诉你如何格式化代码和编译时出现警告,我强烈建议你问问你的导师。
如果使用 gcc,请添加 -Wall -Wextra。对于编码风格,我建议从 Linux or FreeBSD 中窃取一个。有各种编辑器可以为您格式化代码,包括像 vim 这样的真正的编辑器(值得一试,尽管它看起来很苛刻)。
您的编码风格可以帮助您克服困难。
void *count(void*str)
{
MyStruct *struc;
struc = (MyStruct*)str;
const char *myfile = struc->filename;
FILE *f;
int count=0, j;
char buf[50], read[100];
buf 未使用,如果您启用了警告,您将了解到这一点。 read 是个坏名字。
// myfile[strlen(myfile)-1]='[=11=]';
if(!(f=fopen(myfile,"rt"))){
printf("Wrong file name");
}
else
因为您没有 return(您应该有),所以您正在为执行不应该执行的代码而搞砸。猜不到,您的 'else' 子句无效。您缺少大括号,因此即使文件打开操作失败,也会执行下面的 for 循环。
printf("File opened successfully\n");
for(j=0; fgets(read, 10, f)!=NULL; j++){
10?似乎是打字错误,因为您可能是说 100。如果您使用 sizeof,就不会发生这种情况。
if (strcmp(read[j],struc->word)==0)
count++;
}
不清楚你在这里做什么。似乎您想从 read[0]、read1 等开始执行 strcmp。但是你 读取 替换原始缓冲区中的内容的新数据,然后 然后 你将它向前推进一个。这毫无意义。最后,无论如何你做错了。 read[j] 不计算地址,如果您要求,编译器会再一次告诉您。
strcmp 方法无论如何都非常糟糕。尝试一种尝试匹配第一个字符并从那里开始工作的方法。
int main(int argc, char* argv[]) {
标准错位“*”。请改用 char *argv[]。
int i;
MyStruct str;
pthread_mutex_init(&mtx, NULL); // initialize mutex
pthread_t threads[argc-1]; // declare threads array
强烈不推荐。首先验证参数,然后有一个专用变量来保存一定数量的线程。此时就可以分配一个数组了。
for (i=0;i<argc-2;i++){
str.filename = argv[1];
str.word = argv[i+2];
pthread_create(&threads[i], NULL, count, &str);
}
与线程类似,将路径保存在某处。将其称为 argv1 是一种糟糕的风格,它会反过来咬你一口。单词使用 argv 就可以了。
然而,这通常是错误的。您设置一个本地结构并将其传递给一个线程,然后立即更改它。所以发生的事情是在一天结束时你所有的线程都在计算同一个词。但是他们数的单词一直在变化。
for (i = 0; i < argc-1; ++i)
pthread_join(threads[i], NULL);
去图吧。您没有保存一定数量线程的变量,这导致了这种不一致(argc - 1 与 argc - 2)。
一般来说,这个问题在很大程度上可以通过正确阅读编译器警告来解决,如果采用基本的良好做法,通常可以避免。
当然,无论如何都会发生错误,在这种情况下,您至少可以缩小范围。
最后,关于一般方法的几句话。目前还不清楚这次演习的目的是什么。你实际上必须强迫自己使用 pthread_create 和 pthread_join 以外的任何东西。假设唯一的要求是使用线程。
我不知道他们是强制您多次打开文件还是什么。多次打开和阅读内容不仅浪费,而且会导致文件被替换并且某些线程打开另一个文件的情况。
一个 OK 的解决方案是在 main 中打开文件一次。打开后,您将映射文件和 fstat 以获取大小。如果由于某种原因你不能使用 mmap,你会 malloc 一个足够大的缓冲区并读取文件。
然后所有线程都可以获得该缓冲区的地址、要查找的字以及它们应将计数器存储到的地址(每个线程获得不同的地址)。
当所有线程都退出时,您循环对结果求和。
两种方式都不涉及锁定。