[英]c program crashes when retrieving sizeof(buff)
我正在用C创建一个程序,该程序将一个大型文本文件分成10个段,然后创建10个线程,每个线程为每个段生成一个字数统计。 我从以下代码word_count
函数: https : //github.com/prateek-khatri/seaOfC/blob/master/frequencyMultiThread.c 。 该程序对我来说很好用,但是当我尝试在自己的程序中使用word_count
时,尝试获取缓冲区大小时会崩溃。
函数getCurrentSegmentWordcount
似乎一切正常,但是当该函数调用word_count
,它在printf("sizeof Buff: %d", sizeof(buff));
行崩溃(分段错误printf("sizeof Buff: %d", sizeof(buff));
。
#define _GNU_SOURCE
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#define NUMBER_OF_THREADS 10
//struct taken from reference:
struct return_val{
char wordlist[100][100]; //[chars][lines]
int count[100];
} *arr; //array of words
void *print_hello_world(void * tid)
{
//This function prints the thread’s identifier and then exits.
printf("Hello World. Greetings from thread %d\n", tid);
pthread_exit(NULL);
}
void *word_count(void* num)
{
int *ln = num;
unsigned int line_number = *ln;
//line_number++;
printf("Thread %d\n",line_number);
char cmd_p1[9] = "sed -n '\0";
char cmd_p2[2];
sprintf(cmd_p2,"%d",line_number); //stores string in buffer
char cmd_p3[21] = "p' 'maintainers.txt'\0";
char command[100];
command[0] = '\0';
//char * strcat ( char * destination, const char * source );
//appends a copy of source to destination
strcat(command,cmd_p1);
strcat(command,cmd_p2);
strcat(command,cmd_p3);
usleep(line_number);
char cmd[100] = " | tr [:space:] '\\n' | grep -v '^\\s*$' | sort | uniq -c | sort\0";
strcat(command,cmd);
printf("Command: %s\n",command);
//fflush(stdout);
FILE *in;
in= popen(command, "r"); //read command and pipe into the shell
rewind(in); //set file position to beginning of 'in'
char buff[50];
int counter = 0;
//char * fgets ( char * str, int num, FILE * stream );
//reads chars from stream and stores them as string into buff until all of buffer has been read
printf("before\n");
bool testBool = fgets(buff,sizeof(buff),in);
printf("testBool: %d\n", testBool);
//CRASH HAPPENS HERE:
//buff
printf("sizeof Buff: %d", sizeof(buff));
while(fgets(buff,sizeof(buff),in))
{
printf("fire 0.5");
char c=' ';
int i = 0;
int cnt = atoi(buff); //converts string to int.. buff == # of chars in file?
arr[line_number-1].count[counter] = cnt; //at this point line_number == 1
printf("fire1\n");
while(c!='\0')
{
c=buff[i];
buff[i]=buff[i+6];
i++;
}
int cnnt = 0;
while(c!=' ')
{
c = buff[cnnt];
cnnt++;
}
i=0;
while(c!='\0')
{
c=buff[i];
buff[i]=buff[i+cnnt];
i++;
}
sprintf(arr[line_number-1].wordlist[counter],"%s",buff);
printf("%d %s",arr[line_number-1].count[counter],arr[line_number-1].wordlist[counter]);
counter++;
}
printf("final count: %d", counter);
arr[line_number-1].count[counter] = -1;
fclose(in);
//pthread_exit(NULL); //didn't help to move here from getCurrentSegment...()
return NULL;
}
void *getCurrentSegmentWordcount(void * tid) { //declaring file pointer (value?)
int segment = tid;
segment = segment + 1; //converts to int
printf("segment/thread: %d \n", segment);
char text[1000];
//char buffer[150];
FILE *fp = fopen("words.txt", "r");
if(fp == NULL) {
printf("null file");
}
int i = 0;
long lSize;
char *buffer;
if( !fp ) perror("words.txt"),exit(1);
fseek( fp , 0L , SEEK_END);
lSize = ftell( fp );
rewind( fp );
buffer = calloc( 1, lSize+1 );
if( !buffer ) fclose(fp),fputs("memory alloc fails",stderr),exit(1);
if( 1!=fread( buffer , lSize, 1 , fp) )
fclose(fp),free(buffer),fputs("entire read fails",stderr),exit(1);
//printf(buffer);
char *token = strtok(buffer, "~");
if(segment == 1) {
printf("segment 1: %s", token);
word_count(&segment);
}
if(segment == 2) {
token = strtok(NULL,"~");
printf("segment 2: %s", token);
}
if(segment == 3) {
token = strtok(NULL,"~");
token = strtok(NULL,"~");
printf("segment 3: %s", token);
}
if(segment == 4) {
token = strtok(NULL,"~");
token = strtok(NULL,"~");
token = strtok(NULL,"~");
printf("segment 4: %s", token);
}
fclose(fp);
free(buffer);
//pthread_exit(NULL);//moving to end of word_count()
}
int main(int argc, char *argv[])
{
//The main program creates x threads and then exits.
pthread_t threads[NUMBER_OF_THREADS];
int status, i;
for(i=0; i < NUMBER_OF_THREADS; i++) {
printf("Main here. Creating thread %d\n", i+1);
status = pthread_create(&threads[i], NULL, getCurrentSegmentWordcount, (void * )i);
if (status != 0) {
printf("Oops. pthread create returned error code %d\n", status);
exit(-1);
}
}
sleep(8);
exit(NULL);
}
输出:
Main here. Creating thread 1
Main here. Creating thread 2
segment/thread: 1
Main here. Creating thread 3
segment 1: test(segment 1, handled my thread 1)
Thread 1
Main here. Creating thread 4
Command: sed -n '1p' 'maintainers.txt' | tr [:space:] '\n' | grep -v '^\s*$' | sort | uniq -c | sort
Main here. Creating thread 5
segment/thread: 2
before
segment/thread: 4
Main here. Creating thread 6
segment 4:
test test test test (segment 4, handled by thread 4)
Main here. Creating thread 7
segment 2:
test test (segment 2, handled by thread 2)
Main here. Creating thread 8
Main here. Creating thread 9
Main here. Creating thread 10
segment/thread: 3
segment 3:
test test test (segment 3, handled by thread 3)
segment/thread: 10
segment/thread: 9
segment/thread: 8
segment/thread: 5
segment/thread: 6
segment/thread: 7
testBool: 1
Makefile:20: recipe for target 'all' failed
make: *** [all] Segmentation fault (core dumped)
这段代码有很多问题, user3629249已经提到了一些问题,因此我将在这里尝试总结错误。
为线程的参数传递(void * )i
相当难看。 当然可以,但是对我来说这是草率的编程,我将声明一个int
数组,并用id值填充它,然后将一个指针传递给这些位置。
int ids[NUMBER_OF_THREADS];
for(i=0; i < NUMBER_OF_THREADS; i++) {
ids[i] = i+1;
status = pthread_create(&threads[i], NULL, getCurrentSegmentWordcount, ids + i);
...
}
然后在线程函数中:
void *getCurrentSegmentWordcount(void * tid) { //declaring file pointer (value?)
int segment = *((int*) tid);
// segment = segment + 1; not needed anymore
...
}
这段代码更干净,更容易为您和代码审阅者理解,不会依赖丑陋的不必要的强制转换,并且更易于移植。
与...相同
void *print_hello_world(void *tid)
{
//This function prints the thread’s identifier and then exits.
printf("Hello World. Greetings from thread %d\n", tid);
pthread_exit(NULL);
}
这很麻烦,您正在尝试将指针作为int
传递。 指针的大小可能与int
的大小不同。 使用将指针传递给int的相同方法(例如getCurrentSegmentWordcount
):
void *print_hello_world(void *tid)
{
//This function prints the thread’s identifier and then exits.
printf("Hello World. Greetings from thread %d\n", *((int*) tid));
pthread_exit(NULL);
}
将错误消息写入stderr
。 出于这个原因打开了FILE
缓冲区,这就是人们期望程序执行的操作。 执行程序时,可以执行以下操作:
$ program 2>/tmp/error.log
or this
$ program 2>/dev/null | some_other_tool
这样您就可以将正常输出与错误输出分开。
当系统功能失败时, errno
变量将设置为错误代码。 您可以将perror
用作标准错误消息,或者如果需要自定义消息,请使用strerror
:
pid_t p = fork();
if(p < 0)
{
perror("fork failed");
// or
fprintf(stderr, "Error while executing fork: %s\n", strerror(errno));
return; // or exit or whatever
}
如果您想参加C混淆竞赛,则可以在一行中编写代码,否则不要这样做。 对于您而言,这很难读,对于代码审阅者/同事/高级用户而言,这很难读。 您不会从中获得任何收益。
代替
if( !buffer ) fclose(fp),fputs("memory alloc fails",stderr),exit(1);
做
if(buffer == NULL)
{
fclose(fp);
fputs("memory alloc fails", stderr);
exit(EXIT_FAILURE); // or exit(your_exit_status)
}
每个人都更容易阅读。
您应该始终检查返回指针的函数的返回值。 检查malloc
, calloc
, realloc
, strtok
等的返回值。
if(segment == 2) {
token = strtok(NULL,"~");
printf("segment 2: %s", token);
}
如果strtok
返回NULL
,则printf
行将产生未定义的行为。 参见3.5.3.3评论2 :
3.5.3.3 :
概要
#define __STDC_WANT_LIB_EXT1__ 1 #include <stdio.h> int printf_s(const char * restrict format, ...);
[...]
2格式不得为空指针。
%n
说明符(未通过标志,字段宽度或精度进行修改)不得出现在格式所指向的字符串中。 对应于%s
说明符的printf_s
任何参数都不得为空指针 。[...]
4除了上面列出的显式运行时约束,
printf_s
函数与printf
函数等效。
一些libc实现可能会原谅您将NULL
传递给带有%s
printf
和print (null)
,但这不是可移植的,并且是未定义的行为。 因此, 如果token
不为NULL
则只能执行printf
。
word_count
函数有点可怕,尤其是您如何构造命令。
char cmd_p1[9] = "sed -n '\0";
可以改写成
char cmd_p1[] = "sed -n '";
这将创建一个具有正确字节数的char
数组,并使用有效的0终止的字符串对其进行初始化,而无需自己添加' \\0
'。
相同的命令,意味着它们不需要变量值,可以存储在char[]
或const char*
。 然后用snprintf
和sprintf
构造整个东西,减少行数,减少错误:
void *word_count(void* num)
{
...
const char *pipe_cmd = "| tr [:space:] '\\n' | grep -v '^\\s*$' | sort | uniq -c | sort";
const char *format = "sed -n '%dp' 'maintainers.txt' %s";
int cmd_size = snprintf(NULL, 0, format, line_number, pipe_cmd);
char *command = malloc(cmd_size + 1);
if(command == NULL)
return NULL;
sprintf(command, format, line_number, pipe_cmd);
...
FILE *in;
in= popen(command, "r");
free(command);
...
}
另请注意
char cmd_p2[2];
sprintf(cmd_p2,"%d",line_number); //stores string in buffer
如果行号大于9,将使缓冲区溢出。
bool testBool = fgets(buff,sizeof(buff),in);
printf("testBool: %d\n", testBool);
fgets
返回指向char
的指针,而不是bool
。 printf
将指针的值打印为整数。 指针大小不一定与int
大小相同,实际上在我的系统上,指针长8个字节, int
长4个字节。 你应该做:
if(fgets(buff, sizeof(buff), in))
puts("fgets success");
//CRASH HAPPENS HERE:
//buff
printf("sizeof Buff: %d", sizeof(buff));
sizeof
而崩溃。 sizeof
是在编译时而不是在运行时评估的。 sizeof
-operator返回size_t
。 %d
不是size_t
的正确说明符, %lu
是,应该是
printf("sizeof buff: %lu\\n", sizeof buff);
由于此之前所有未定义的行为,它很可能崩溃。
arr[line_number-1].count[counter] = cnt;
在您的整个代码中, arr
是未初始化的,因此您正在通过未初始化的指针访问值。 这是未定义的行为,可能会导致段错误。
我想在这里引用user3629249 :
user3629249写道 :
main()
函数启动多个线程,然后立即退出。 退出过程还消除了线程建议:在main()
pthread_join()
为每个线程调用pthread_join()
。 在线程中,最后,调用pthread_exit()
请不要忽略编译器警告,它们不是在烦扰您,它们是在帮助您。 这些提示表明您在做什么可能不是您真正想要的。 不确定的行为,段错误等通常是这种情况的结果。 因此,请注意编译器的警告,当您看到一个警告时,请查看您的代码,尝试理解并修复它。 如果您不明白该警告,则可以到这里询问有关此问题的信息。 但是,如果有成千上万的警告而无视它们,则会导致头痛,并且坦率地说,浪费在您和我们这边的时间。
因此,请修复所有这些警告和详细信息,查看编译器的警告消息,然后代码可能没有问题地运行。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.