Попытка понять эту ошибку сегментации - PullRequest
1 голос
/ 18 января 2012

Я пытаюсь понять, почему я получаю ошибку (segfault) в указанной строке моего кода ниже (отмечено: <<<SEGFAULT OCCURS HERE).Я написал это с большим вдохновением из этого поста .

Я считаю, что это проблема выделения памяти, но, учитывая, что даже когда я передаю указатель экземпляра Event в функцию enqueue, он. ошибка сегментацияУчитывая, что C передается по значению, даже когда я передаю адрес события (&event не показан в коде, который я здесь разместил) в main функции 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;
}

Заранее спасибо!

Ответы [ 3 ]

3 голосов
/ 18 января 2012
Event **tail = NULL;

tail равно NULL, и вы разыменовываете его в отмеченной вами строке.Это должно быть сделано, чтобы указать на Event*, прежде чем вы можете разыменовать его:

Либо

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

Или

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

... 

free(tail);

Хотя, я думаю, вы имеете в видуиметь только указатель на Event и передавать его адрес по значению:

Event *tail = NULL, *head = NULL;

...

enqueue(event, &head, &tail);

, чтобы head и tail были изменены внутри enqueue.

0 голосов
/ 18 января 2012

Ваш код содержит как минимум две ошибки:

  1. Как указали другие, вы передаете нулевой указатель на хвост, и разыменование вызовет ошибку segfault
  2. Вы передадите1006 * по значению.Это означает, что копия структуры будет создана и помещена в стек.Внутри функции вы делаете *head = &newEvent, который берет адрес объекта, который находится в стеке, и помещает его в очередь.После того, как вы вернетесь из функции enqueue, стек будет очищен и вы укажете на мусор.Вам нужно изменить eneueue на int enqueue(Event *newEvent, Event **head, Event **tail) и передать указатель на событие.

Я не уверен, почему вы используете указатель на указатель, и ваша очередь кажется семантически разбитой (головаобычно начало и 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 для выделения памяти для него)

0 голосов
/ 18 января 2012

Вы разыменовываете нулевой указатель.

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