繁体   English   中英

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

[英]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)
}

每个人都更容易阅读。


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

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* 然后用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));
  1. 它不会因为sizeof而崩溃。 sizeof是在编译时而不是在运行时评估的。
  2. sizeof -operator返回size_t
  3. %d不是size_t的正确说明符, %lu是,应该是

     printf("sizeof buff: %lu\\n", sizeof buff); 
  4. 由于此之前所有未定义的行为,它很可能崩溃。


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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM