#include <stdio.h>
#include <stdlib.h>
#include <time.h>
// 定义结构体
typedef struct tystr
{
int number; // 存储数据的数组
struct tystr *next; // 指向下一个节点的指针
} tystr, *tystr_ptr;
//创建一个节点,且此节点指向下一个是NULL
tystr_ptr creatnode(void)
{
tystr_ptr p = (tystr_ptr)malloc(sizeof(tystr));
p->next = NULL;
return p;
}
//在node前,插入一个新节点,返回新节点的指针
tystr_ptr insertnode(tystr_ptr node)
{
tystr_ptr p = creatnode();
node->number = rand() % 100;
node->next = p; //覆盖原来指向NULL,重新指向为createnode生成的节点
return p;
}
int main(void)
{
time_t t;
srand((unsigned)time(&t));
tystr_ptr head = creatnode();
tystr_ptr p = head;
p = insertnode(p);
p = insertnode(p);
p = insertnode(p);
p = insertnode(p);
p = head;
while (p->next != NULL)
{
printf("number:%d\n", p->number);
p = p->next;
}
return 0;
}
定义结构体:
// 定义结构体
typedef struct tystr
{
int number; // 存储数据的数组
struct tystr *next; // 指向下一个节点的指针
} tystr, *tystr_ptr;
创建节点函数:
//创建一个节点,且此节点指向下一个是NULL
tystr_ptr creatnode(void)
{
tystr_ptr p = (tystr_ptr)malloc(sizeof(tystr));
p->next = NULL;
return p;
}
插入节点函数:
//在node前,插入一个新节点,返回新节点的指针
tystr_ptr insertnode(tystr_ptr node)
{
tystr_ptr p = creatnode();
node->number = rand() % 100;
node->next = p; //覆盖原来指向NULL,重新指向为createnode生成的节点
return p;
}
每次调用 createnode()
时,都会创建一个新节点,并将其 next
指针初始化为 NULL
。然而,在代码的实际运行过程中,这些 NULL
并不会一直保留,而是会被后续的操作覆盖。下面详细解释这些 NULL
的去向以及链表的构建过程。
1. createnode()
的行为
tystr_ptr creatnode(void)
{
tystr_ptr p = (tystr_ptr)malloc(sizeof(tystr));
p->next = NULL; // 新节点的 next 指针初始化为 NULL
return p;
}
-
每次调用
createnode()
时,都会创建一个新节点,并将其next
指针初始化为NULL
。 -
这意味着,每个新节点在创建时都是一个独立的节点,没有连接到任何其他节点。
2. insertnode()
的行为
tystr_ptr insertnode(tystr_ptr node)
{
tystr_ptr p = creatnode(); // 创建一个新节点
node->number = rand() % 100; // 为当前节点的 number 赋值随机数
node->next = p; // 将当前节点的 next 指向新节点
return p; // 返回新节点的指针
}
-
在
insertnode()
中,新创建的节点会被连接到当前节点的next
指针。 -
具体来说:
-
调用
createnode()
创建一个新节点p
,其next
指针为NULL
。 -
将当前节点
node
的next
指针指向新节点p
。 -
返回新节点
p
的指针。
-
-
关键点:
-
新节点的
next
指针在创建时是NULL
,但在insertnode()
中,它会被连接到当前节点的next
指针。 -
因此,新节点的
next
指针的NULL
值会被覆盖,除非它是链表的最后一个节点。
-
3. 链表的构建过程
让我们通过一个具体的例子来说明链表的构建过程,以及 NULL
的去向。
(1) 初始状态
-
调用
head = creatnode();
创建头节点head
。 -
此时链表状态:
head -> NULL
-
head
的next
指针为NULL
。
-
(2) 第一次调用 p = insertnode(p);
-
p
初始指向head
。 -
调用
insertnode(p)
:-
创建一个新节点
node1
,其next
指针为NULL
。 -
将
head
的next
指针指向node1
。 -
返回
node1
的指针,并将其赋值给p
。
-
-
此时链表状态:
head -> node1 -> NULL
-
node1
的next
指针为NULL
。
-
(3) 第二次调用 p = insertnode(p);
-
p
现在指向node1
。 -
调用
insertnode(p)
:-
创建一个新节点
node2
,其next
指针为NULL
。 -
将
node1
的next
指针指向node2
。 -
返回
node2
的指针,并将其赋值给p
。
-
-
此时链表状态:
head -> node1 -> node2 -> NULL
-
node2
的next
指针为NULL
。
-
(4) 第三次调用 p = insertnode(p);
-
p
现在指向node2
。 -
调用
insertnode(p)
:-
创建一个新节点
node3
,其next
指针为NULL
。 -
将
node2
的next
指针指向node3
。 -
返回
node3
的指针,并将其赋值给p
。
-
-
此时链表状态:
head -> node1 -> node2 -> node3 -> NULL
-
node3
的next
指针为NULL
。
-
4. NULL
的去向
-
每次调用
createnode()
时,新节点的next
指针确实被初始化为NULL
。 -
但是,在
insertnode()
中,新节点会被连接到当前节点的next
指针,因此新节点的next
指针的NULL
值会被覆盖。 -
只有在链表的最后一个节点中,
next
指针才会保持为NULL
,表示链表的结束。
5. 总结
-
每次调用
createnode()
时,新节点的next
指针确实被初始化为NULL
。 -
在
insertnode()
中,新节点会被连接到当前节点的next
指针,因此新节点的next
指针的NULL
值会被覆盖。 -
只有在链表的最后一个节点中,
next
指针才会保持为NULL
,表示链表的结束。
6. 验证代码
如果你希望验证链表的最后一个节点的 next
指针是否为 NULL
,可以在遍历链表时添加以下代码:
p = head;
while (p != NULL)
{
printf("number:%d\n", p->number);
if (p->next == NULL)
{
printf("This is the last node, next is NULL.\n");
}
p = p->next;
}
-
这段代码会打印每个节点的
number
值,并在最后一个节点时输出提示信息。