C:带链接列表实现的奇怪SegFault

时间:2017-03-16 18:10:27

标签: c linked-list queue mergesort timsort

编辑:部分解决。我按照第一个答案的建议将NULL检查切换为while循环检查中的第一个,但是我仍然无法访问除第一个之外的任何队列元素。我可以访问data-> head,但我无法访问data-> head-> next,除非我专门创建一个指向data-> head-> next的节点结构。如果我特意创建该节点(让我们称之为temp),那么temp-> next也是不可访问的。基本上,当我将队列传递给此函数时,>下一个功能完全丢失,我不明白为什么。

我正在为Uni开展一个学校项目,我们在其中构建一个修改后的mergesort(编辑:一位评论者注意到这是TimSort的一种形式),而不是普通的mergesort算法,它将数据集划分为已经排序的块数据,然后重新组合它们理论上减少运行时间 作为集合

的示例
3 4 5 2 9 4 3 2 

将分成

[3 4 5] [2 9] [4 3 2]  

然后按排序顺序合并在一起。我们的特定实现必须使用链表,队列和堆栈来完成 这是我的链表定义和队列定义:

typedef struct node_t
{
   int value;

   struct node_t * next;
} node_t;

typedef struct queue_t
{
   node_t *head, *tail;

} queue_t;

我遇到问题的代码是在标识“运行”的函数中,或者是已经对数据进行排序的数据区域。它将队列作为输入,迭代并识别第一个节点是否小于或大于下一个节点。然后它选择运行哪个循环并将每个值出列到一个新队列中,然后将其作为“运行”返回,该运行是已排序数据的子集。
出于某种原因,每当我尝试使用data-> head-> next或data-> head-> next->值时,它会引发分段错误,即使我知道该节点存在且值存在。我一直在通过gdb运行我的程序来找到错误点,我在Windows上,我不能使用valgrind。 这是我遇到问题的函数的代码:

queue_t * extract_next_run(queue_t * data)
{
    queue_t * queue = queue_create();

int ascend = 0;

//when there is only one value in the list
if(data->head->next = NULL)
{
    queue_enqueue(queue, queue_dequeue(data));
    return queue;
}


//ascending run *CRASHES ON THIS WHILE LOOP*
while(data->head->value <= data->head->next->value && data->head->next != NULL)
{   
    queue_enqueue(queue, queue_dequeue(data));

    //not sure if this improves runtime over just "ascend = 1" every iteration
    if(ascend != 1)
        ascend = 1;
}

//descending run
while(data->head->value > data->head->next->value && data->head->next != NULL)
{   
    queue_enqueue(queue, queue_dequeue(data));
}

if(ascend == 0)
    queue_reverse(queue);

queue->tail->next = NULL;
return queue;
}

这就是让我难以理解的原因:

//things I have tested in this function

printf("%d", data->head->value); // works fine
printf("%d", data->head->next->value); // segmentation fault
data->head = data->head->next; //segmentation fault    

if(data->head->next == NULL) //works fine

node_t * temp = data->head->next; // works fine
printf("%d", temp->value); // works fine
temp = temp->next; //segmentation fault  

另一件令我烦恼的事情是我能够在其他函数中使用queue-&gt; head = queue-&gt; head-&gt; next并且它工作正常,我可以使用node = node-&gt; next它工作正常,所以我真的不知道问题是什么。我知道这个函数调用的函数有效,但我很乐意为它们提供代码。这里有声明,如果需要我可以提供我的所有代码(不想让这个问题太长):

// mallocs a queue and sets its head and tail to NULL
queue_t * queue_create();  

// takes a value and adds it to the end of the queue
void queue_enqueue(queue_t * queue, int data);

// removes the first node of the queue and returns its value
int queue_dequeue(queue_t * queue);

// takes a queue, reads it into a stack, 
// then uses stack_pop to return values in reverse order
void queue_reverse(queue_t * queue);

我将非常感谢任何帮助,我一直在努力弄清楚问题是什么,但到目前为止我没有成功。提前谢谢!

2 个答案:

答案 0 :(得分:3)

你已经将问题缩小到这个循环:

while(data->head->value <= data->head->next->value && data->head->next != NULL) {

实际上,您似乎正在指责循环控制表达式。那么考虑一下这个表达式:为什么它包含data->head->next != NULL的测试?

考虑当条件的这一部分为假时 - 即data->head->next == NULL为真时会发生什么。在首次评估&&的左侧操作数(包括评估data->head->next->value)之前,程序不会(永远)评估该条件,并且当data->head->next为空时会产生未定义的行为。您可能根本没有进行空检查,实际上,某些编译器可能会对其进行优化。

要使空检查有效,必须在 之前对其进行评估,以保护其中的任何评估。

答案 1 :(得分:0)

我修复了它,我的函数中的第一个if语句是if(queue->head->next = NULL),但我应该if(queue->head->next == NULL)。我使用了赋值运算符=而不是比较运算符==,它将queue->head->next分配给NULL,这导致了段错误。