C和指针 (pointers on C)——第十二章:使用结构和指针

第十二章 使用结构和指针

这章就是链表。先单链表,后双向链表。

总结:

单链表是一种使用指针来存储值的数据结构。链表中的每个节点包含一个字段,用于指向链表的下一个节点。

有一个独立的根指针指向链表的第1个节点。单链表只能从一个方向遍历。

如何insert单链表:1、新节点的link字段必须设置为指向它的后面节点。2、前一个节点的link字段必须指向这个新节点。

为了防止可能会插入链表的起始位置这种情况,在C中,可以保存一个指向必须进行修改的link字段的指针,而不是保存一个指向前一个节点的指针。

双链表中的每个节点包含两个link字段:其中一个指向链表的下一个node,另一个指向前一个node。

双链表有两个根指针,一个指向第一个node,另一个指向最后一个node。因此遍历的过程中可以从任何一端开始,而且在遍历过程中够可以改变方向。

为了把一个新节点插入到双链表中,我们必须修改4个指针。新节点的前向和后向link字段必需被设置,前一个节点的fwd和后一个节点的bwd也要修改,指向新节点。

警告:

1、落到链表尾部的后面。

2、使用指针时应该格外小心,因为C并没有对他们的使用提供安全网。

3、从if语句中提炼语句可能会改变测试结果。

编程提示:

1、消除特殊情况使代码更易于维护。

2、不要轻易的进行提炼语句,这样会使你的语句更难维护。

编程实例:(本章就不再弄习题了,关于数据结构这块会有大量代码进行训练)

1、提炼后的单链表插入操作

#include "stdlib.h"

typedef struct NODE
{
	struct NODE *link;
	int			value;
} Node;
int sll_int(register Node **linkp, int new_value)
{
	register Node *current;		//指向当前节点
	register Node *new_node;	//指向插入节点

	/*
	** 寻找正确插入位置,按顺序访问链表,直到有个值大于或等于新值
	*/
	while ((current = current->link) != NULL && current->value < new_value)
	{
		linkp = ¤t->link; //移动linkp指向下一个Node的link
	}
	/* 分配新的内存,并存到新节点去 */
	new_node = (NODE *) malloc (sizeof(NODE));
	if (new_node == NULL)
	{
		return 0;
	}
	new_node->value = new_value;
	/* 插入新节点 */
	new_node->link = current;
	*linkp = new_node;
	return 1;
}

2、双链表插入操作

#include "stdlib.h"

typedef struct NODE
{
	struct NODE *fwd;
	struct NODE *bwd;
	int value;
}Node;

int dll_insert(Node *rootp, int value)
{
	/* 把一个值插入到一个双向链表中,rootp是一个指向根节点的指针
	   value 是插入的新值
	   返回值:如果已经存在链表中,返回0
	         如果内存不足导致无法插入,返回-1,成功返回1;
	*/
	Node *this_node;
	Node *next_node;
	Node *new_node;

	for (this_node = rootp; next_node != NULL; this_node = next_node )
	{
		if (next_node->value == value)
			return 0;
		if (next_node->value < value)
			break;
		next_node = next_node->fwd;
	}
	/* 为新节点申请内存空间*/
	new_node = (Node *) malloc (sizeof(Node));
	if (new_node == NULL)
		return -1;
	new_node->value = value;
	/*
		插入节点
	    if 不在链表尾部 then 不在链表起始位置 or 位于链表起始位置
		else 在链表尾部 then 不在链表起始位置 or 位于链表起始位置(空链表)
	*/
	if (next_node->fwd != NULL)
	{
		/*不在链表尾部*/
		if (this_node != rootp)
		{
			/* 不在链表的头部 */
			this_node->fwd = new_node;
			next_node->bwd = new_node;
			new_node->bwd = this_node;
			new_node->fwd = next_node;
		}
		else
		{
			/* 在链表的头部*/
			rootp->fwd = new_node;
			next_node->bwd = new_node;
			new_node->bwd = rootp;
			new_node->fwd = next_node;
		}
	}
	else
	{
		/*在链表尾部*/
		if (this_node->bwd != rootp)
		{
			/* 不在链表的头部 */
			new_node->fwd = NULL;
			new_node->bwd = this_node;
			this_node->fwd = new_node;
			rootp->bwd = new_node;
		}
		else
		{
			/* 在链表的头部*/
			new_node->fwd = NULL;
			new_node->bwd = NULL;
			rootp->bwd = new_node;
			rootp->fwd = new_node;
		}
	}

}
时间: 2024-12-22 07:34:41

C和指针 (pointers on C)——第十二章:使用结构和指针的相关文章

C和指针 第十二章 使用结构和指针

链表是一种常用的数据结构,每个节点通过链或者指针链接在一起,程序通过间接指针访问链表中的节点. typedef struct Node { //指向下一个节点的指针 struct Node *next; int value; } 单链表只可以单向遍历 单链表中插入:第一版 #include <stdio.h> #include <stdlib.h> #define TRUE 1 #define FALSE 0 typedef struct Node { struct Node *n

C和指针 (pointers on C)——第十二章:利用结构和指针

第十二章 利用结构和指针 这章就是链表.先单链表,后双向链表. 总结: 单链表是一种使用指针来存储值的数据结构.链表中的每一个节点包括一个字段,用于指向链表的下一个节点. 有一个独立的根指针指向链表的第1个节点. 单链表仅仅能从一个方向遍历. 怎样insert单链表:1.新节点的link字段必须设置为指向它的后面节点. 2.前一个节点的link字段必须指向这个新节点. 为了防止可能会插入链表的起始位置这样的情况,在C中,能够保存一个指向必须进行改动的link字段的指针.而不是保存一个指向前一个节

perl5 第十二章 Perl5中的引用/指针

第十二章 Perl5中的引用/指针 by flamephoenix 一.引用简介二.使用引用三.使用反斜线(\)操作符四.引用和数组五.多维数组六.子程序的引用  子程序模板七.数组与子程序八.文件句柄的引用 一.引用简介    引用就是指针,可以指向变量.数组.哈希表(也叫关联数组)甚至子程序.Pascal或C程序员应该对引用(即指针)的概念很熟悉,引用就是某值的地址,对其的使用则取决于程序员和语言的规定.在Perl中,可以把引用称为指针,二者是通用的,无差别的.引用在创建复杂数据方面十分有用

C primer plus 第五版十二章习题

看完C prime plus(第五版)第十二章,随带完成了后面的习题. 1.不使用全局变量,重写程序清单12.4的程序. 先贴出12.4的程序,方便对照: 1 /* global.c --- 使用外部变量 */ 2 #include <stdio.h> 3 int units = 0; //一个外部变量 4 void critic(void); 5 int main(void) 6 { 7 extern int units; 8 9 printf ("How many pounds

第二十二章 TCP/IP层的实现

                      第二十二章    TCP/IP层的实现        我比较喜欢先难后易,如果把GPU显示管理.和网络管理拿下后:我会从头整理.改写一遍APO操作系统.这样,就会形成APO操作系统的锥形.也获得了全局观.内核CPU线路.和用户CPU线路,你可以将它们看成是独立的2个32位CPU核:内核CPU主要任务是实时处理.硬件中断,256个实时线程包含了一些中断程序的后半部.用户CPU主要是动态优先级进程.线程调度,各种应用程序的运行:2个核之间是通过消息交互.句

第十二章 APO编程语言

第十二章      APO编程语言 APO编程语言是基于汇编语言和面向对象编程.基本指令只有7种:赋值指令.COPY指令.BTX(位X测试为1.或0转移)指令.查表跳转指令switch(RN){-.}.移位与循环指令S.三操作数运算指令.调用与返回指令.所有的指令大小.除了32位立即数赋值是2字外:其它都是32位,一个字. 指令执行时间,除了32位立即数赋值是2ns.COPY指令取决于拷贝的长度外:其它指令都是1ns. 应用程序只能使用R0-R4,R8-R15的21个寄存器作为高速的寄存器局部变

[CSAPP笔记][第十二章并发编程]

第十二章 并发编程 如果逻辑控制流在时间上是重叠,那么它们就是并发的(concurrent).这种常见的现象称为并发(concurrency). 硬件异常处理程序,进程和Unix信号处理程序都是大家熟悉的例子. 我们主要将并发看做是一种操作系统内核用来运行多个应用程序的机制. 但是,并发不仅仅局限于内核.它也可以在应用程序中扮演重要的角色. 例如 Unix信号处理程序如何允许应用响应异步事件 例如:用户键入ctrl-c 程序访问虚拟存储器的一个未定义的区域 其他情况 访问慢速I/O设备 当一个应

第十二章 并发编程 学习笔记

第十二章 并发编程 进程是程序级并发,线程是函数级并发. 三种基本的构造并发程序的方法: 进程:每个逻辑控制流是个一个进程,由内核进行调度和维护. I/O多路复用:应用程序在一个进程的上下文中显式地调度他们自己的逻辑流. 线程:运行在单一进程上下文中的逻辑流,由内核进行调度. 12.1 基于进程的并发编程 构造并发程序最简单的方法就是用进程. 使用大家都很熟悉的函数例如: fork exec waitpid 关于在父.子进程间共享状态信息:共享文件表,但不共享用户地址空间. 进程又独立的地址空间

第十二章 并发编程

第十二章 并发编程 三种基本的构造并发程序 进程:每个逻辑控制流是一个进程,由内核进行调度,进程有独立的虚拟地址空间 I/O多路复用:逻辑流被模型化为状态机,所有流共享同一个地址空间 线程:运行在单一进程上下文中的逻辑流,由内核进行调度,共享同一个虚拟地址空间 常用函数: fork exec waitpid 基于I/O多路复用的并发事件驱动服务器 事件驱动程序:将逻辑流模型化为状态机. 状态机: 状态 输入事件 转移 对于状态机的理解,参考EDA课程中学习的状态转换图的画法和状态机. 整体的流程