我一直在试图弄清楚在C中的双向链表冒泡排序中,我在哪里解引用了一个空指针。当特别添加while(p2->next!=NULL)
时,我得到Exception thrown: read access violation.p2->**next** was 0xFFFFFFFFFFFFFFFF.
node* swap(node* p1, node* p2)
{
p1->next = p2->next;
p2->next->prev = p2->prev;//Exception thrown: read access violation.
// p2->**next** was 0xFFFFFFFFFFFFFFFF.
free(p2);
return p1;
}
void sort_list(node** head, int n) {
node** ptr = head;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n - i - 1; j++) {
node* p1 = *ptr;
node* p2 = p1->next;
while (p2->next != NULL) {
if (p1->data > p2->data) {
p1=swap(p1, p2);
}
else { p1 = p1->next;}
}
}
}
}
有没有人可以给予我一个提示,如何解决这个问题?(也许reccomend的东西,我可以读了关于这个主题,因为我自己学习,我没有任何人来帮助我,当我卡住了)我也得到了一对夫妇的警告解除引用空指针在函数中创建头节点和其他函数,创建其余的节点
node* add_to_empty(node* head, int data)//creates the 1st node and returns head pointer to it
{
node* temp = (node*)malloc(sizeof(node));
temp->prev = NULL;
temp->data = data;
temp->next = NULL;
head = temp;
return head;
}
node* add_at_beg(node* head, int data)//adds node in front of the last one
{
node* temp = (node*)malloc(sizeof(node));
temp->prev = NULL;
temp->data = data;
temp->next = NULL;
temp->next = head;
head->prev = temp;
head = temp;
return head;}
通常如何避免这些警告?
3条答案
按热度按时间kiz8lqtg1#
我只想看看
swap
,因为这是你问的错误,因为那里有很多东西要解包。代码的其余部分也好不到哪里去,但我不得不把它排除在讨论范围之外。正常情况
为了交换p1和p2,我们需要更新
需要八个任务,但你只有两个!
我们还需要考虑边缘情况。有两个:
因为p1和p2总是不同的,并且因为p2在列表中总是晚于p1,所以p1->next和p2->prev都不会为NULL。
如果p1和p2是相邻的,有些赋值将是冗余的,但这不是特殊处理这种情况的理由。
p1是列表的第一个节点
为了交换p1和p2,我们需要更新
p2是列表的最后一个节点
为了交换p1和p2,我们需要更新
请记住,这两种边缘情况可能发生在同一个调用中。
为了交换p1和p2,我们需要更新
cu6pst1q2#
指针取消引用通常是由这样的代码片段引起的:
如果
p1->next
是NULL
呢?在这种情况下,您将NULL
赋给p2
,然后 * 立即使用表达式p2->next
解除引用 *。一个常用的模式来防止这类问题:
&&
运算符的一个好处是它“短路”-如果左手表达式(p2 != NULL
)的计算结果为false,则整个表达式的计算结果必须为false,因此 * 右手表达式不计算 *。换句话说,如果指针为NULL,则不会取消引用。我并不是说这是您的特定用例的正确逻辑,但是当使用可能为NULL的指针时,此模式作为基本工具非常有用。
wfsdck303#
你的舞蹈有很多问题。
从
swap()
开始:关于
sort_list()
:while
循环条件取决于p2->next
,但p2->next
不会在该循环中被修改,除非*p2
可能被释放(见上文)。老实说,那里没什么值得保存的。我会把它扔掉,从头开始。在此过程中考虑
1.通过临时给列表一个虚拟的头节点,可以在很大程度上避免特殊情况。框架:
这样,您的真实的头节点就有了一个临时的前身。
1.正如已经暗示的那样,不要依赖于外部提供的列表长度。您可以通过维护一个指针来表示排序后的尾列表的头,并在到达当前尾列表的前一个时终止每个交换传递(然后成为尾列表的新头)。然后,您将知道,当真实的列表头(如果您实现(1),则为伪头的后继者)也是尾列表的头时,没有更多的传递要做。
1.代替一般的交换,实现一个
swap_with_next()
,这稍微简单一点。如果你实现了(1),那么swap_with_next()
不需要考虑null前导的可能性,但是它仍然需要注意next->next
是否为null。