在转置链表和链表排序的时候遇到了问题,出现问题的地方在于指针指向了一块已经被free了的内存空间,然在下次malloc动态申请空间之前都能正常的访问,但是只要有动态申请空间,再次访问已经被free的空间的时候就出现段错误。
问题描述
h是一个一级指针,指向了一个带头节点的链表;现在有一个转置链表的函数,将链表再执行一段头插法,而现在这个新链表的头节点的指针是t,我想要h指向这个转置成功的链表。
不同的处理方法
1 h指向被释放的空间
(*h) = t; //h是一个二级指针
free(t); //t是一个一级指针,是链表的头节点
t = NULL;
上述是将h的地址当作参数传了进来,这里的h是一个二级指针;
(*h) = t是将h指向了t,但是后来t所指向的节点被free了,t也指向NULL了,但是(*h)还指向这个节点。当被free掉的这片空间没有被从新分配的时候还是可以正常访问的,一但当则块空间被分配,那么你再次访问就可能是段错误或者是得到了一个错误的结果;
2 正确的方法
(*h)->next = t->next; //h是一个二级指针
free(t); //t是一个一级指针,是链表的头节点
t = NULL;
这个也是所使用的方法没有改变h的指向;
并且在释放t所指向的空间前,使用(*h)->next指向了后面的链表,这样就达到了期望,并且不会出现段错误。
3 h-> next 为NULL
h = t; //h是一个节点
free(t); //t是一个一级指针,是链表的头节点
t = NULL;
这里的h传进来的是一个节点的地址,是一个一级指针;相当于值传递,在这里修改h的指向是不会改变实参的。
4 h->next 为NULL
h = t->next; //h是一个节点
free(t); //t是一个一级指针,是链表的头节点
t = NULL;
同上,在这里修改形参不会改变实参,想要形参应该实参,那么需要传递一个二级指针。
5 正确的方法
h->next = t->next;//h是一个节点
free(t); //t是一个一级指针,是链表的头节点
t = NULL;
这也是正确的,这里并没有直接修改实参,只是修改了实参指向的空间的内容,所以能达到期望。
涉及到的代码
我觉得上面描述的可能有点不清楚,我将代码放到下面,供大家讨论:
main.c
#include "list.h"
int main(){
linklist_t *h = LinkListCreate();
//采用尾插法保持链表中的数据和数组中数据的相对位置一致
// LinkListCreateall(h);
for(int i = 1; i <= 8; i++){
if(LinkListInsertTail(h,i) == -1){
printf("插入失败");
return -1;
}
}
LinkListShow(h);
// ReverseLinkList1(&h); //代码1 2
ReverseLinkList2(h); //代码3 4 5
LinkListShow(h);
return 0;
}
list.h
#ifndef __LIST_H__
#define __LIST_H__
#include <stdio.h>
#include <stdlib.h>
#define datatype int
typedef struct node{
datatype data;
struct node *next;
}linklist_t;
linklist_t *LinkListCreate(void);
int LinkListInsertTail(linklist_t *h,datatype data);
int LinkListIsEmpty(linklist_t *h);
void ReverseLinkList1(linklist_t **h);
void ReverseLinkList2(linklist_t *h);
void LinkListShow(linklist_t *h);
#endif
list.c
#include "list.h"
/*
*功能:创建循环链表
*参数:
* @:无
*返回值:成功返回单链表的首地址,失败返回NULL
*注:创建链表头,将指针域指向NULL,将数据域设置0
*/
linklist_t* LinkListCreate(void)
{
linklist_t* h;
h = (linklist_t*)malloc(sizeof(*h));
if (h == NULL) {
printf("alloc memory error\n");
return NULL;
}
h->data = (datatype)0;
h->next = NULL;
return h;
}
/*
*功能:单链表按照尾插法插入数据
*参数:
* @h:单链表的指针
* @data:被插入的数据
*返回值:成功返回0,失败返回-1
*/
int LinkListInsertTail(linklist_t* h, datatype data)
{
linklist_t *tmp, *th = h;
// 1.分配临时的节点,将data放入到节点的数据域中
if ((tmp = (linklist_t*)malloc(sizeof(*tmp))) == NULL) {
printf("malloc memory error\n");
return -1;
}
tmp->data = data;
while (h->next != NULL)
h = h->next;
tmp->next = h->next;
h->next = tmp;
return 0;
}
/*
*功能:链表的遍历
*参数:
* @h:链表的指针
*返回值:无
*/
void LinkListShow(linklist_t* h)
{
// 1.如果链表为空,直接返回
if (LinkListIsEmpty(h))
return;
// 2.如果链表不为空,挨个成员访问
while (h->next) {
h = h->next;
printf("-%d", h->data);
}
printf("-\n");
}
/*
*功能:判断单链表是否为空
*参数:
* @h:链表的指针
*返回值:空返回1,否则返回0
*/
int LinkListIsEmpty(linklist_t* h)
{
return h->next == NULL ? 1 : 0;
}
/*
*功能:将链表的逆序
*参数:
* @h:链表头节点的地址
*返回值:无
*/
void ReverseLinkList1(linklist_t** h)
{
linklist_t* t = LinkListCreate();
linklist_t* tmp = NULL;
while ((*h)->next) { // h->next != NULL
tmp = (*h)->next;
(*h)->next = tmp->next;
tmp->next = t->next;
t->next = tmp;
}
(*h)->next = t->next; //代码2 切记不要写成 *h = t;
//(*h) = t; //代码1
tmp = NULL;
free(t);
t = NULL;
}
void ReverseLinkList2(linklist_t* h)
{
linklist_t* t = LinkListCreate();
linklist_t* tmp = NULL;
while ((h)->next) { // h->next != NULL
tmp = (h)->next;
(h)->next = tmp->next;
tmp->next = t->next;
t->next = tmp;
}
// h = t; //代码3
// h = t -> next; //代码4
(h)->next = t->next; //代码5 切记不要写成 *h = t;
tmp = NULL;
free(t);
t = NULL;
}