试图理解这种分段错误

时间:2012-01-17 23:35:20

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

我试图理解为什么我在下面的代码指示行中得到段错误(注意:<<<SEGFAULT OCCURS HERE)。我用this post的灵感来写这篇文章。

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

请注意我正在寻找更多原因,而不仅仅是修复问题。毕竟我正在研究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;
}

提前致谢!

3 个答案:

答案 0 :(得分:3)

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);

以便在head内修改tailenqueue

答案 1 :(得分:0)

您正在取消引用空指针。

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.
    }
}

答案 2 :(得分:0)

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

  1. 正如其他人指出的那样,你传入一个NULL指针用于尾部和解除引用将导致段错误
  2. 您按价值传递newEvent。这意味着将生成结构的副本并将其放在堆栈中。在函数内部执行*head = &newEvent,它获取堆栈中对象的地址并将其放入队列中。从enqueue函数返回后,堆栈将被清理并指向垃圾。您需要将eneueue更改为int enqueue(Event *newEvent, Event **head, Event **tail)并传递指向该事件的指针。
  3. 我不确定为什么你使用指针指针,你的队列似乎在语义上被破坏(头部通常是开始,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);
    

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