gpt4 book ai didi

c - 检索sizeof(buff)时c程序崩溃

转载 作者:行者123 更新时间:2023-11-30 14:50:51 25 4
gpt4 key购买 nike

我正在C中创建一个程序,该程序将一个大型文本文件分为10个段,然后创建10个线程,每个线程为每个段生成一个字数。我从以下代码中获取了功能word_counthttps://github.com/prateek-khatri/seaOfC/blob/master/frequencyMultiThread.c。该程序对我来说很好用,但是当我尝试在自己的程序中使用word_count时,在尝试获取缓冲区大小时崩溃。

函数getCurrentSegmentWordcount似乎一切正常,但是当该函数调用word_count时,它会在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)
}


每个人都更容易阅读。



您应该始终检查返回指针的函数的返回值。
检查 malloccallocreallocstrtok等的返回值。

if(segment == 2) {
token = strtok(NULL,"~");
printf("segment 2: %s", token);
}


如果 strtok返回 NULL,则 printf行将产生未定义的行为。
参见 3.5.3.3 comment 2


   3.5.3.3
  
  概要

      #define __STDC_WANT_LIB_EXT1__ 1
#include <stdio.h>
int printf_s(const char * restrict format, ...);

  
  [...]
  
  2格式不得为空指针。 %n说明符(通过标志,字段宽度或精度修改或不修改)不应出现在格式所指向的字符串中。对应于 printf_s说明符的 %s的任何参数均不得为空指针。
  
  [...]
  
  4 printf_s函数与 printf函数等效,除了上面列出的显式运行时约束。


一些libc实现可能会原谅您使用 NULLprintf传递给 %s
并打印 (null),但这不是可移植的,并且行为不确定。那么你
如果 printf不是 token,则只能执行 NULL



word_count函数有点可怕,特别是您如何构造
命令。

char cmd_p1[9] = "sed -n '\0";


可以改写成

char cmd_p1[] = "sed -n '";


这将创建具有正确字节数的 char数组并初始化
它带有一个以0终止的有效字符串,不需要自己添加' \0'。

相同的命令,这意味着它们不需要来自
变量可以存储在 char[]甚至 const char*中。然后构造
snprintfsprintf的全部内容,更少的行,更少的错误:

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运算符返回一个 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_exit()




请不要忽略编译器警告,它们不会惹恼您,它们是
在那里帮你。这些提示表明您在做什么可能与您不同
真的想要。未定义的行为,段错误等通常是由于
那。因此,请注意编译器的警告,并在看到警告时查看代码,
尝试了解并修复它。如果您不明白警告,可以
来这里问一个问题。但是有成千上万的警告和
忽略它们会导致头痛,坦白地说,这浪费了很多时间
您的一面和我们的一面。

因此,请修复所有这些警告和详细信息,然后查看以下警告消息:
编译器和代码可能没有问题地运行。

关于c - 检索sizeof(buff)时c程序崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48726328/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com