【问题标题】:Trying to Understand this Segmentation Fault试图理解这个分段错误
【发布时间】:2012-01-17 23:35:20
【问题描述】:

我试图了解为什么我在下面的代码中指定的行出现段错误(注明:<<<SEGFAULT OCCURS HERE)。我写这篇文章的灵感来自this post

我认为这是一个内存分配问题,但考虑到即使我将 Event 实例的指针传递给 enqueue 函数,它仍然会出现段错误。考虑到 C 是按值传递的,即使我将 main 中的事件地址(&event 未在我发布的代码中显示)传递给 enqueue 函数,它也应该指向 main 中存在的事件实例的地址,对?所以我很难理解为什么会发生分段错误。

请注意,我更多的是寻找发生这种情况的原因,而不仅仅是解决问题。毕竟,我正在努力刷新 C 语言。 :)

相关代码:

typedef struct Event_ Event;
struct Event_ {
    char action[4];
    long timestamp;
    char* path;
    char hash[9];
    Event *nextEvent; // pointer to next Event instance in Queue
};    

    // Enqueues newEvent into queue. Returns 1 on success, 0 otherwise.
int enqueue(Event newEvent, Event **head, Event **tail) {
    if (head != NULL) {
        // make the old head point to the newly inserted Event, 
        // and the new Event to point to NULL (nothing comes before head):
        (*head) -> nextEvent = &newEvent;
        newEvent.nextEvent = NULL;
    } else {
        // first element being added to queue.
        *tail = &newEvent; //<<<SEGFAULT OCCURS HERE
    }
    // designate the new Event as the new head:
    *head = &newEvent;

    return 1;
}

// Parse line and return an Event struct.
Event parseLineIntoEvent(char* line) {
    Event event = {0};
    char* lineSegment;

    int i = 0;
    lineSegment = strtok(line, " ");
    while (lineSegment != NULL) {
        if (i > 3) {
            printf("WARNING: input format error!\n");
            break;
        }
        if (i == 0)
            strncpy(event.action, lineSegment, sizeof(event.action)-1);
        else if(i == 1)
            event.timestamp = atoi(lineSegment);
        else if(i == 2) {
            event.path = malloc(sizeof(char) * (strlen(lineSegment) + 1));
            strcpy(event.path, lineSegment);
        } else if(i == 3)
            strncpy(event.hash, lineSegment, sizeof(event.hash)-1);
        lineSegment = strtok(NULL, " ");
        i++;
    } // while
    return event;
} // parseLineIntoEvent()

int main (int argc, const char * argv[]) {
    //...
    Event **head = NULL;
    Event **tail = NULL;
    for (; numLines > 0; numLines--) {
        char *line = getLineFromStdin(); //malloced char array being returned
        printf("%s\n",line);
        Event event = parseLineIntoEvent(line);
        if(!enqueue(event, head, tail))
            printf("An error occurred when attempting to enqueue an Event.\n");
        event = dequeue(head, tail);
        //...
        free(event.path);
        free(line);
    }

    return 0;
}

提前致谢!

【问题讨论】:

  • enqueue 函数已损坏。你通过值传递newEvent,这不会修改你传入的事件。

标签: c memory-management queue segmentation-fault parameter-passing


【解决方案1】:
Event **tail = NULL;

tailNULL,并且您在标记的行上取消引用它。在取消引用之前,它需要指向 Event*

要么

Event *ev;
Event **tail = &ev;

或者

Event** tail = malloc(sizeof(Event*));

... 

free(tail);

不过,我认为您的意思是只有一个指向 Event 的指针,并按值传递其地址:

Event *tail = NULL, *head = NULL;

...

enqueue(event, &head, &tail);

这样headtailenqueue 内部被修改。

【讨论】:

  • 问题标记为C;所以new 不合适。
  • @Dave 抱歉,我总是忘记我在哪个标签中。对于此类愚蠢的错误,请随时编辑我的答案。
【解决方案2】:

您正在取消引用一个空指针。

int main (int argc, const char * argv[]) {
    //...
    Event **head = NULL;
    Event **tail = NULL;
    for (; numLines > 0; numLines--) {
        //...
        if(!enqueue(event, head, tail)) // head and tail are NULL here
        //...
    }
}

int enqueue(Event newEvent, Event **head, Event **tail) {
    if (head != NULL) {  // head is NULL, else path will be executed
        //...
    } else {
        // first element being added to queue.
        *tail = &newEvent; //<<<SEGFAULT OCCURS HERE because tail is null.
    }
}

【讨论】:

    【解决方案3】:

    您的代码至少包含两个错误:

    1. 正如其他人所指出的,您正在为尾部传递一个 NULL 指针,而取消引用将导致段错误
    2. 你按值传入newEvent。这意味着将制作结构的副本并将其放入堆栈。在函数内部,您执行*head = &amp;newEvent,它获取堆栈上对象的地址并将其放入队列中。从入队函数返回后,堆栈将被清理并指向垃圾。您需要将 eneueue 更改为 int enqueue(Event *newEvent, Event **head, Event **tail) 并传递指向事件的指针。

    我不确定你为什么使用指向指针的指针,而且你的队列似乎在语义上被破坏了(head 通常是开头,enqueue 通常在末尾追加内容)

    int enqueue(Event *event, Event **head, Event **tail)
    {
        if (*head != NULL)
        {
            // we do have a head: point next to current head
            event->nextEvent = *head;
        }
        else
        {
            // no head so we also should have no tail
            *tail = event;
            event->nextEvent = NULL;
        }
    
        // newly enqueued event is now head
        *head = event;
    
        return 1;
    }
    

    使用它:

    Event *head = NULL;
    Event *tail = NULL;
    
    ...
    
    Event newEvent = parseLineIntoEvent(char* line);
    enqueue(&newEvent, &head, &tail);
    

    虽然您应该考虑通过从parseLineIntoEvent 返回Event *newEvent 放在堆上(使用malloc 为其分配内存)

    【讨论】:

      猜你喜欢
      • 2019-09-20
      • 2017-05-12
      • 2021-07-29
      • 2019-09-22
      • 1970-01-01
      • 2019-08-30
      • 2019-09-06
      • 2019-02-25
      • 1970-01-01
      相关资源
      最近更新 更多