C语言中单链表的创建中的地址传递和打印以及逆置详解

插法创建单链表:

首先要定义一个单链表,其中typedef关键字是用来给数据类型重命名的。我这里命名为LNode,LinkList,其中LinkList为声明的头指针L。
注意:
在声明函数的时候,LNode与LinkList等价,不同的是LinkList强调这是链表,LNode强调这是节点。

typedef struct LNode {//定义单链表节点类型
	int data;//每个节点存放一个数据元素
	struct LNode* next;//指针指向下一个节点
}LNode,*LinkList;

下面是用尾插法创建一个单链表,首先要初始化一个单链表,然后每取一个数据,我们需要把这个数据插入到表尾:(其中代码中我创建的单链表是带头节点的)
下图中,我为了方便观看每取一次数据我换了一个颜色,并进行了相应的编号区分方便大家通过看图更加全面的理解下面的代码尤其是r->next= s和r=s这两句
C语言中单链表的创建中的地址传递和打印以及逆置详解
值得注意的是:

  1. 声明一个新指针并且要存数据的时候,记得考虑内存分配失败的情况,否则将会报错为"取消对null指针"xx"的引用"
  2. LinkList List_TailInsert(LinkList *L) 这里我用到了地址传递,记得返回值要为&L,所以再后来我在main函数里调用为List_TailInsert(&L)
  3. 地址传递就相当于把某个值变成了全局变量,所以我们需要通过地址来进行形参的定义;值传递就相当于局部变量,只改变当前函数里的形参的值,并不影响实参的值。另外:c语言里的地址传递相当于c++中的&引用调用一样。
LinkList List_TailInsert(LinkList *L) {
	int x;
	//*L = NULL;
	*L = (LinkList)malloc(sizeof(LNode));//初始化空表
	if (L==NULL) {//考虑内存分匹配失败的情况
		return 0;
	}
	LNode *s,*r = *L;//定义两个指针使他们分别都和头指针指向相同
	scanf_s("%d", &x);
	while (x!=9999)
	{
		s = (LNode *)malloc(sizeof(LNode));//给s分配一个新的节点
		if (s == NULL) {//考虑内存分匹配失败的情况
			return 0;
		}
		s->data = x;//把x赋值给新的节点
		if (r == NULL) {//考虑内存分匹配失败的情况
			return 0;
		}
		r->next= s;//让r的下一个指针与s指针指向相同,也就是在r节点后插入s
		r=s;//永远保持r只想最后一个节点
		printf("--------------------");
		scanf_s("%d", &x);
		
	}
	if (r == NULL) {
		return 0;
	}
	r->next = NULL;

	return *L;
}

下面就是打印单链表和主函数里的相应调用了:

void GetElem(LinkList L) {
	LNode* p;
	int j = 0;
	p = L;
	while (p!=NULL)
	{
		p = p->next;
		printf("输入的值为:%d,地址为:%p
", p->data, p);
	}
 }

int main() {
	LinkList L;
	L=List_TailInsert(&L);
	GetElem(L);
}

全部代码:

#include <stdio.h>
#include <stdlib.h>

typedef struct LNode {
	int data;
	struct LNode* next;
}LNode,*LinkList;

LinkList List_TailInsert(LinkList *L) {
	int x;
	//*L = NULL;
	*L = (LinkList)malloc(sizeof(LNode));
	if (L==NULL) {
		return 0;
	}
	LNode *s,*r = *L;
	scanf_s("%d", &x);

	while (x!=9999)
	{
		s = (LNode *)malloc(sizeof(LNode));
		if (s == NULL) {
			return 0;
		}
		s->data = x;
		if (r == NULL) {
			return 0;
		}
		r->next= s;
		r=s;
		printf("--------------------");
		scanf_s("%d", &x);
		
	}
	if (r == NULL) {
		return 0;
	}
	r->next = NULL;

	return *L;
}

void GetElem(LinkList L) {
	LNode* p;
	int j = 0;
	p = L;
	while (p!=NULL)
	{
		p = p->next;
		printf("输入的值为:%d,地址为:%p
", p->data, p);
	}
 }

int main() {
	LinkList L;
	L=List_TailInsert(&L);
	GetElem(L);
}

结果:
C语言中单链表的创建中的地址传递和打印以及逆置详解
不创建头结点的尾插法创建单链表的方法:
值得注意的是,打印单链表的函数中,不能先让p指针指向p的下一个指针指向的位置,需要先打印再进行此操作

#include <stdio.h>
#include <stdlib.h>

typedef struct LNode {
	int data;
	struct LNode* next;
}LNode, * LinkList;

LinkList List_TailInsert(LinkList* L) {
	int x;
	int i = 0;
	*L = NULL;
	//*L = (LinkList)malloc(sizeof(LNode));
	//if (L == NULL) {
		//return 0;
	//}
	LNode * s, * r = *L;
	//LNode* s = *L;
	scanf_s("%d", &x);
	s = (LNode*)malloc(sizeof(LNode));
	if (s == NULL) {
		return 0;
	}
	s->data = x;
	*L = s;//L指针指向s指针所指向的节点
	r = *L;.//r指针指向L指针所指向的节点,这里指向s也是一个道理
	printf("^^^^^^^^^^^^^^^");
	scanf_s("%d", &x);
	while (x != 9999)//里面的循环就和有头结点的循环一样了
	{	
		s = (LNode*)malloc(sizeof(LNode));
		if (s == NULL) {
			return 0;
		}
		s->data = x;
		if (r == NULL) {
			return 0;
		}
		r->next = s;
		r = s;
		printf("--------------------");
		scanf_s("%d", &x);

	}
	if (r == NULL) {
		return 0;
	}
	r->next = NULL;

	return *L;
}

//打印单链表
void GetElem(LinkList L) {
	LNode* p;
	int j = 0;
	p = L;
	while (p != NULL)
	{
		printf("输入的值为:%d,地址为:%p
", p->data, p);
		p = p->next;
		
	}
}

int main() {
	LinkList L;
	L = List_TailInsert(&L);
	GetElem(L);
}

结果:
C语言中单链表的创建中的地址传递和打印以及逆置详解

头插法建立单链表:

首先需要初始化单链表,然后每取一次数据就向表头插一个数据。
值得注意的是,这里的指针L要表示指针L的下一个指针时需要带括号,否则就会报错"表达式必须包含指向结构或联合的指针类型"

#include <stdio.h>
#include <stdlib.h>

typedef struct LNode {
	int data;
	struct LNode* next;
}LNode, * LinkList;

LinkList List_HeadInsert(LinkList* L) {
	LNode* s;
	int x;
	*L = (LinkList)malloc(sizeof(LNode));//创建头结点
	(*L)->next = NULL;//初始为空链表
	scanf_s("%d", &x);
	while (x != 9999)
	{
		s = (LNode*)malloc(sizeof(LNode));
		if (s == NULL) {
			return 0;
		}
		s->data = x;
		s->next = (*L)->next;//s指针的下一个指针指向l的下一个指针,也就是s指针的下一个指针指向null
		(*L)->next = s;//l指针的下一个指针指向s指针的所指的方向
		printf("--------------------");
		scanf_s("%d", &x);

	}
	return *L;
}

void GetElem(LinkList L) {
	LNode* p;
	int j = 0;
	p = L;
	while (p!=NULL)
	{
		p = p->next;
		printf("输入的值为:%d,地址为:%p
", p->data, p);
	}
 }

int main() {
	LinkList L;
	L = List_HeadInsert(&L);
	GetElem(L);
}

结果:
C语言中单链表的创建中的地址传递和打印以及逆置详解
由此可看,单链表的数据是逆置的,所以我们需要考虑单链表逆置

单链表逆置

下图是单链表逆置的图像解析,为了方便更好理解代码,这个思路有点像后插法,我们需要声明两个指针变量。一个用来引导指针L一个用来寻找后续节点。
C语言中单链表的创建中的地址传递和打印以及逆置详解
代码如下:

LinkList Reverse(LinkList L) {
	LNode * p,* r;
	p = L->next;
	L->next = NULL;//断链

	while (p!=NULL)
	{
		r = p->next;
		p->next = L->next;
		L->next = p;
		p = r;
	}
	return L;
}

综合上述,头插法建立单链表的全部代码为:

#include <stdio.h>
#include <stdlib.h>

typedef struct LNode {
	int data;
	struct LNode* next;
}LNode, * LinkList;

LinkList List_HeadInsert(LinkList* L) {
	LNode* s;
	int x;
	*L = (LinkList)malloc(sizeof(LNode));//创建头结点
	(*L)->next = NULL;//初始为空链表
	scanf_s("%d", &x);
	while (x != 9999)
	{
		s = (LNode*)malloc(sizeof(LNode));
		if (s == NULL) {
			return 0;
		}
		s->data = x;
		s->next = (*L)->next;//s指针的下一个指针指向l的下一个指针,也就是s指针的下一个指针指向null
		(*L)->next = s;//l指针的下一个指针指向s指针的所指的方向
		printf("--------------------");
		scanf_s("%d", &x);

	}
	return *L;
}

void GetElem(LinkList L) {
	LNode* p;
	int j = 0;
	p = L;
	while (p!=NULL)
	{
		p = p->next;
		printf("输入的值为:%d,地址为:%p
", p->data, p);
	}
 }
 //单链表逆置
LinkList Reverse(LinkList L) {
	LNode * p,* r;
	p = L->next;
	L->next = NULL;//断链

	while (p!=NULL)
	{
		r = p->next;
		p->next = L->next;
		L->next = p;
		p = r;
	}
	return L;
}

int main() {
	LinkList L;
	L = List_HeadInsert(&L);
	L = Reverse(L);
	GetElem(L);
}

结果为:
C语言中单链表的创建中的地址传递和打印以及逆置详解