单链表的增删改查(数据结构)

news2024/12/23 3:09:47

之前我们学习了动态顺序表,今天我们来讲一讲单链表是如何进行增删改查的

一、单链表

1.1、单链表概念

概念:链表是⼀种物理存储结构上⾮连续、⾮顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

1.2、链表与顺序表的区别:

链表不像顺序一样,空间给少了不够,给多了浪费,链表是你需要多少空间,就申请多少空间。

链表中一个结点就是一个数据,结点中必须包含两个数据,一个是我们需要存放的数据,另外一个就是下一个数据的地址。

每存放一个数据,我们就需要申请一个结点,直到最后一个结点指向的地址为NULL

淡季时⻋次的⻋厢会相应减少,旺季时⻋次的⻋厢会额外增加⼏节。只需要将⽕⻋⾥的某节⻋厢去掉/加上,不会影响其他⻋厢,每节⻋厢都是独⽴存在的。

在链表⾥,每节“⻋厢”是什么样的呢?

1.3、链表的打印

为什么提前讲链表是如何打印的,而不是先讲插入删除,初始化,这是因为比较好理解,这个理解的,接下来在看增删查改理解就会快了。

当pcur为头结点时,需要打印第一个结点的数据,第一个结点的数据为pcur这个结构体中的data数据,所有我们需要获取data数据进行打印。当需要打印第二个结点里data数据时候,pcur需要往前走一个结点,走到的结点是pcur结构体中存放的下一个结点的地址进行解引用,然将解引用得到的数据赋值给pcur,此时pcur就往前前进了一位。直到pcur等于NULL才停止循环。

二、增删改查

在这一部分,只写node.c中增删改查实现的函数代码,总代码可在最后进行查看。

2.1、结点的创建(结构体)

node.h

typedef int SLTDataType;     //将int类型定义为SLTDataType,方便修改数据类型

typedef struct SListNode {
	SLTDataType data;       
	struct SListNode* next;    //指向的下一个结点
}SLTNode;

struct SListNode* next;这段代码是创建一个为类型大小为struct SListNode指针的next,next指向下一个结点,为什么不是这样写struct SListNode next;  如果这样的话,每个结点都包含一个完整的 SListNode ,那么造成了巨大的空间浪费,在进行移动的时候,占用非常大的计算。

这样写struct SListNode* next; 那么在进行两个结点连接时,next可以存储另外一个结点的地址,可以快速找到下一个数据

2.2、创建头结点

test.c

int main()
{
	SLTNode* plist = NULL;   //创建头结点
    return 0;
}

为什么需要使用SLTNode* plist = NULL; 那是因为plist是一个指向SLTNode的一个指针,我们可以用这个来指向链表的头结点,并且在链表的动态中进行改变他的值。

SLTNode plist = NULL;这样写的意思是plist的类型是SLTNode并且等于NULL,这是一个极其错误的写法。

所以如果我们想要动态的使用结点,就需要创建一个头结点指向SLTNode,这样就可以使用结构体中的数据。

那为什么不这样写 SLTNode plist;然后对结构体进行初始化。那是因为,我们所创建的不是一个顺序表,我们需要记录下一个空间存入的数据与该空间的下一个结点。

2.3、尾插

void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	//pphead --> &plist
	// *pphead --> plist
	//申请新节点
	SLTNode* newnode = SLTBuyNode(x);
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		//找尾结点
		SLTNode* pcur = *pphead;
		while (pcur->next)
		{
			pcur = pcur->next;
		}
		//pcur  newnode
		pcur->next = newnode;
	}
}

void SLTPushBack(SLTNode** pphead, SLTDataType x)注意这里使用了二级指针。

为什么要使用二级指针?

由于我们需要修改头指针的内容,而传递指针本质上是值传递,所以我们需要通过二级指针来直接操作头指针的地址,才能真正修改头指针的值。

在顺序表中,由于你是在固定大小的数组上操作,数组的大小和起始地址是固定的,不需要动态分配和改变,所以不需要二级指针。

在链表中,如果直接将创建的头结点的值传入函数中,那么就相当于传值调用,在程序运行完以后,栈帧也会消失,所以需要取地址进行函数调用,这样头结点内容就被修改了。

2.4、申请新结点

//申请新节点
SLTNode* SLTBuyNode(SLTDataType x)
{
	SLTNode* node = (SLTNode*)malloc(sizeof(SLTNode));
	if (node == NULL)
	{
		perror("malloc fail!");
		exit(1);
	}
	node->data = x;
	node->next = NULL;

	return node;
}

在链表中使用malloc来扩容,而非增容(realloc),当创建好一个新结点时,把数据(x)存放到新结点中的data里面,新结点指向的下一个指针next先置为空,在接下来增删改查时候再做修改。

2.5、尾插

//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);                     //判断pphead是否为NULL
	//申请新节点
	SLTNode* newnode = SLTBuyNode(x);   //创建一个新结点
	if (*pphead == NULL)
	{
		*pphead = newnode;      //如果*pphead是已经初始化为NULL,那么直接将新结点等于头结点
	}
	else
	{
		//找尾结点
		SLTNode* pcur = *pphead;     //创建一个新指针等于pphead的
		while (pcur->next)           //pcur->next不等于NULL就一直运行
		{
			pcur = pcur->next;       //一直将pcur往后遍历,直到pcur->next等于NULL
		}
		pcur->next = newnode;        //将原链表pcur->next指向NULL的结点指针指向新的结点
	}
}

为什么我们不适用*pphead来遍历链表,因为我们在打印链表的时候,需要从头结点开始遍历,如果使用*pphead来遍历的话,此时*pphead就走到了链表的末尾,在打印链表的时候就无法打印,所以就需要创建一个指针等于*pphead,借助新创建的指针来进行链表的修改,当新指针走到了链表的末尾,*pphead还是在链表的头结点。

2.6、头插

//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);

	SLTNode* newnode = SLTBuyNode(x);  //创建新结点
	newnode->next = *pphead;           //将新创建的头结点中指向下一个结点的指针直接指向*pphead
	*pphead = newnode;                 //将*pphead等于newnode,此时头结点还是*pphead
} 

2.7、尾删

//尾删
void SLTPopBack(SLTNode** pphead)
{
	//链表为空:不可以删除
	assert(pphead && *pphead);
	//处理只有一个结点的情况:要删除的就是头结点	
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		//找 prev ptail
		SLTNode* ptail = *pphead;
		SLTNode* prev = NULL;
		while (ptail->next)
		{
			prev = ptail;
			ptail = ptail->next;
		}
		prev->next = NULL;
		free(ptail);
		ptail = NULL;
	}
}

............................................

第四个图中,newtail->next = NULL,此时循环停止,将prev->next置为NULL,此时prev就是最后一个结点,接着将不需要的结点newtail中的空间给释放掉.

2.8、头删

//头删
void SLTPopFront(SLTNode** pphead)
{
	assert(pphead && *pphead);

	SLTNode* next = (*pphead)->next;//记录头结点指向的下一个结点
	free(*pphead);   //释放头结点
	*pphead = next;   //将*pphead等于刚刚记录下来的结点,此时就完成了头结点的删除操作
}

2.9、查找链表中的数据

//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
	assert(phead);
	SLTNode* pcur = phead;
	while (pcur)
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	//没有找到
	return NULL;
}

当链表中存放的数据等于x时,返回pcur,否则就返回NULL

2.10、在指定位置之前插⼊数据

//在指定位置之前插⼊数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead);
	assert(pos);        //查找的数据不能为空,需要是一个有效的数

	if (pos == *pphead)
	{
		SLTPushFront(pphead, x);  如果pos等于头结点,那么直接进行头插
	}
	else
	{
		SLTNode* newnode = SLTBuyNode(x);
		//找prev :pos的前一个结点
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		newnode->next = pos;
		prev->next = newnode;
	}
}

例如需要查找的数是pos=3,prev->next中的地址解引用得到的结果就是3,所以prev停止pos结点之前的一个结点。

将newnode->next指针指向pos,接着将prev->next指向newnode。这样就完成了链表的插入。

2.11、在指定位置之后插⼊数据

//在指定位置之后插⼊数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
	assert(pos);
	SLTNode* newnode = SLTBuyNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

例如pos=2,那么图就是这这样的

2.12、删除pos结点

//删除pos结点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead && *pphead);
	assert(pos);

	//头删
	if (pos == *pphead)
	{
		SLTPopFront(pphead);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
}

2.13、删除pos之后的结点

//删除pos之后的结点
void SLTEraseAfter(SLTNode* pos)
{
	assert(pos && pos->next);
	SLTNode* del = pos->next;
	pos->next = pos->next->next;
	free(del);
	del = NULL;
}

2.14、销毁链表

//销毁链表
void SListDestroy(SLTNode** pphead)
{
	assert(pphead && *pphead);

	SLTNode* pcur = *pphead;
	while (pcur)
	{
		SLTNode* next = pcur->next;    //遍历每一个结点,将每一个结点空间进行释放
		free(pcur);
		pcur = next;                   //将释放后的结点,等于下一个结点,直到pcur=NULL
	}
	*pphead = NULL;                    //释放完全部结点以后,将*pphead也置为NULL
}

三、全部源代码

node.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

//定义链表(结点)的结构
typedef int SLTDataType;

typedef struct SListNode {
	SLTDataType data;
	struct SListNode* next;
}SLTNode;

void SLTPrint(SLTNode* phead);

//插入
void SLTPushBack(SLTNode** pphead, SLTDataType x);
void SLTPushFront(SLTNode** pphead, SLTDataType x);

//删除
void SLTPopBack(SLTNode** pphead);
void SLTPopFront(SLTNode** pphead);

//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);

//在指定位置之前插⼊数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//在指定位置之后插⼊数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x);

//删除pos结点
void SLTErase(SLTNode** pphead, SLTNode* pos);
//删除pos之后的结点
void SLTEraseAfter(SLTNode* pos);
//销毁链表
void SListDestroy(SLTNode** pphead);
test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "node.h"


void SListTest01()
{
	SLTNode* plist = NULL;
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);
	SLTPrint(plist);//1->2->3->4->NULL

	//SLTPushBack(NULL, 3);
	//SLTPushFront(&plist, 1);
	//SLTPushFront(&plist, 2);
	//SLTPushFront(&plist, 3);
	//SLTPushFront(&plist, 4);
	//SLTPrint(plist); //4->3->2->1->NULL

	//SLTPopBack(&plist);
	//SLTPrint(plist);
	//SLTPopBack(&plist);
	//SLTPrint(plist);
	//SLTPopBack(&plist);
	//SLTPrint(plist);
	//SLTPopBack(&plist);
	//SLTPrint(plist);
	//SLTPopBack(&plist);
	//SLTPrint(plist);
	//
	//SLTPopFront(&plist);
	//SLTPrint(plist);
	//SLTPopFront(&plist);
	//SLTPrint(plist);
	//SLTPopFront(&plist);
	//SLTPrint(plist);
	//SLTPopFront(&plist);
	//SLTPrint(plist);
	//SLTPopFront(&plist);
	//SLTPrint(plist);

	//SLTNode* find = SLTFind(plist, 4);
	//if (find == NULL)
	//{
	//	printf("未找到!\n");
	//}
	//else
	//{
	//	printf("找到了!\n");
	//}
	//SLTInsert(&plist, find, 11);//4->3->2->11->1->NULL
	//SLTInsertAfter(find, 11);
	//SLTPrint(plist);1->11->2->3->4->NULL

	//SLTErase(&plist, find);// 1->2->3->NULL
	//SLTEraseAfter(find);

	//SLTPrint(plist);
	SListDestroy(&plist);
	SLTPrint(plist);
}

int main()
{
	SListTest01();
	return 0;
}
node.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "node.h"

void SLTPrint(SLTNode* phead)
{
	SLTNode* pcur = phead;
	while (pcur)
	{
		printf("%d->", pcur->data);
		pcur = pcur->next;
	}
	printf("NULL\n");
}

//申请新节点
SLTNode* SLTBuyNode(SLTDataType x)
{
	SLTNode* node = (SLTNode*)malloc(sizeof(SLTNode));
	if (node == NULL)
	{
		perror("malloc fail!");
		exit(1);
	}
	node->data = x;
	node->next = NULL;

	return node;
}

void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	//pphead --> &plist
	// *pphead --> plist
	//申请新节点
	SLTNode* newnode = SLTBuyNode(x);
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		//找尾结点
		SLTNode* pcur = *pphead;
		while (pcur->next)
		{
			pcur = pcur->next;
		}
		//pcur  newnode
		pcur->next = newnode;
	}
}
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);

	SLTNode* newnode = SLTBuyNode(x);
	//newnode *pphead
	newnode->next = *pphead;
	*pphead = newnode;
}
void SLTPopBack(SLTNode** pphead)
{
	//链表为空:不可以删除
	assert(pphead && *pphead);
	//处理只有一个结点的情况:要删除的就是头结点	
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		//找 prev ptail
		SLTNode* ptail = *pphead;
		SLTNode* prev = NULL;
		while (ptail->next)
		{
			prev = ptail;
			ptail = ptail->next;
		}
		prev->next = NULL;
		free(ptail);
		ptail = NULL;
	}
}
void SLTPopFront(SLTNode** pphead)
{
	assert(pphead && *pphead);

	SLTNode* next = (*pphead)->next;
	//*pphead --> next
	free(*pphead);
	*pphead = next;
}
//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
	assert(phead);
	SLTNode* pcur = phead;
	while (pcur)
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	//没有找到
	return NULL;
}
//在指定位置之前插⼊数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead);
	assert(pos);

	if (pos == *pphead)
	{
		SLTPushFront(pphead, x);
	}
	else
	{
		SLTNode* newnode = SLTBuyNode(x);
		//找prev :pos的前一个结点
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		//prev newnode --> pos
		newnode->next = pos;
		prev->next = newnode;
	}
}
//在指定位置之后插⼊数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
	assert(pos);
	SLTNode* newnode = SLTBuyNode(x);
	//pos newnode --> pos->next
	newnode->next = pos->next;
	pos->next = newnode;
}
//删除pos结点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead && *pphead);
	assert(pos);

	//头删
	if (pos == *pphead)
	{
		SLTPopFront(pphead);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		//prev pos pos->next
		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
}
//删除pos之后的结点
void SLTEraseAfter(SLTNode* pos)
{
	assert(pos && pos->next);

	//pos pos->next pos->next->next
	SLTNode* del = pos->next;
	pos->next = pos->next->next;
	free(del);
	del = NULL;
}
//销毁链表
void SListDestroy(SLTNode** pphead)
{
	assert(pphead && *pphead);

	SLTNode* pcur = *pphead;
	while (pcur)
	{
		SLTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	*pphead = NULL;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2184941.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大豆重测序二(同一领域互竞)-文献精读58

High-quality genome of a modern soybean cultivar and resequencing of 547 accessions provide insights into the role of structural variation 现代大豆品种的高质量基因组及对547个种质资源的重测序揭示结构变异的作用 大豆重测序-文献精读53 摘要 大豆提供蛋白质、油…

Qemu开发ARM篇-7、uboot以及系统网络连接及配置

文章目录 1、uboot及linux版本网络设置1、宿主机虚拟网卡创建2、uboot使用tap0网卡3、启动测试 2、访问外网设置 在上一篇Qemu开发ARM篇-6、emmc/SD卡AB分区镜像制作并通过uboot进行挂载启动中&#xff0c;我们制作了AB分区系统镜像&#xff0c;并成功通过uboot加载kernel以及d…

基于Springboot+Vue的美妆神域(含源码数据库)

1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 系统中…

基于STM32的智能停车管理系统

目录 引言项目背景环境准备 硬件准备软件安装与配置系统设计 系统架构关键技术代码示例 传感器数据读取模块停车位控制模块OLED显示状态应用场景结论 1. 引言 智能停车管理系统旨在提高停车场的管理效率&#xff0c;减少车主寻找停车位的时间。该系统通过传感器实时监测停车…

Spring Boot技术在足球青训管理中的实践与挑战

摘 要 随着社会经济的快速发展&#xff0c;人们对足球俱乐部的需求日益增加&#xff0c;加快了足球健身俱乐部的发展&#xff0c;足球俱乐部管理工作日益繁忙&#xff0c;传统的管理方式已经无法满足足球俱乐部管理需求&#xff0c;因此&#xff0c;为了提高足球俱乐部管理效率…

深圳大学 Github 学生认证并免费使用 Copilot AI编程工具(超详细)

文章目录 01 注册学生邮箱并添加邮箱到Github1.1 注册学生邮箱1.2 绑定学生邮箱 02 修改 Github profile 信息03 申请学生认证[^2]04 配置 Copliot05 VS code 使用 Copilot 01 注册学生邮箱并添加邮箱到Github 1.1 注册学生邮箱 对于深圳大学的学生来说&#xff0c;学校已经帮…

【CSS in Depth 2 精译_042】6.4 CSS 中的堆叠上下文与 z-index(下)——深入理解堆叠上下文

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第一章 层叠、优先级与继承&#xff08;已完结&#xff09;第二章 相对单位&#xff08;已完结&#xff09;第三章 文档流与盒模型&#xff08;已完结&#xff09;第四章 Flexbox 布局&#xff08;已…

聊聊国内首台重大技术装备(2)

上次&#xff0c;介绍了《首台&#xff08;套&#xff09;重大技术装备推广应用指导目录&#xff08;2024年版&#xff09;》中介绍的硅外延炉&#xff0c;湿法清洗机&#xff0c;氧化炉&#xff0c;见文章&#xff1a; 《聊聊国内首台重大技术装备&#xff08;1&#xff09;》…

ESP32微信小程序SmartConfig配网

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 ESP32&微信小程序SmartConfig配网 前言一、SmartConfig是什么&#xff1f;二、使用乐鑫官方的smart_config例子1.运行照片 三、微信小程序总结 前言 本人是酷爱ESP32S3这…

数据结构--包装类简单认识泛型

目录 1 包装类 1.1 基本数据类型和对应的包装类 1.2 装箱和拆箱&#xff0c;自动装箱和自动拆箱 2 什么是泛型 3 引出泛型 3.1 语法 4 泛型类的使用 4.1 语法 4.2 示例 5 泛型的上界 5.1 语法 5.2 示例 5.3 复杂示例 8 泛型方法 8.1 定义语法 8.2 示例 总结 1 …

【web安全】——XSS漏洞

1.XSS漏洞基础 1.1.漏洞成因 XSS(Cross-site scripting)被称为跨站脚本攻击&#xff0c;由于与层叠样式表的缩写一样&#xff0c;因此被缩写为XSS.XSS漏洞形成的原因是网站/程序对前端用户的输入过滤不严格&#xff0c;导致攻击者可以将恶意的is/html代码注入到网页中&#x…

LeetCode[中等] 763. 划分字母区间

给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段&#xff0c;同一字母最多出现在一个片段中。 注意&#xff0c;划分结果需要满足&#xff1a;将所有划分结果按顺序连接&#xff0c;得到的字符串仍然是 s 。 返回一个表示每个字符串片段的长度的列表。 思路 贪心…

JavaWeb酒店管理系统(详细版)

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

QT 界面编程中使用协程

QT 界面编程中使用协程 一、概述二、集成2.1、编译 Acl2.2、将 Acl 库集成到 QT 项目中2.3、开始编写代码2.3.1、QT 程序初始化时初始化 Acl 协程2.3.2、在界面中创建协程2.3.3、界面程序退出前需要停止协程调度2.3.4、在界面线程中下载数据2.3.5、在协程中延迟创建窗口 2.4、效…

8642 快速排序

### 思路 快速排序是一种分治算法&#xff0c;通过选择一个基准元素将数组分成两部分&#xff0c;然后递归地对每部分进行排序。每次分区后输出当前排序结果。 ### 伪代码 1. 读取输入的待排序关键字个数n。 2. 读取n个待排序关键字并存储在数组中。 3. 对数组进行快速排序&am…

HarmonyOS/OpenHarmony 自定义弹窗页面级层级控制解决方案

关键词&#xff1a;CuntomDialog自定义弹窗、SubWindow子窗口、页面级、弹窗层级控制、鸿蒙、弹窗展示层级异常 问题存在API版本&#xff1a;API10 - API12&#xff08;该问题已反馈&#xff0c;期望后续官方能增加页面级控制能力&#xff09; 在正常的鸿蒙app开发过程中&…

【Linux】命令管道

一、命名管道的介绍 之前的管道博客中介绍的是匿名管道&#xff0c;这个管道的应用的一个限制就是只能在具有公共祖先&#xff08;具有亲缘关系&#xff09;的进程间通信。 如果我们不想在不相关的进程之间交换数据&#xff0c;可以使用FIFO文件来做这项工作&#xff0c;他经常…

Arduino UNO R3自学笔记6 之 Arduino引脚(IO)功能介绍

注意&#xff1a;学习和写作过程中&#xff0c;部分资料搜集于互联网&#xff0c;如有侵权请联系删除。 前言&#xff1a;Ardunio UNO R3有很多引脚&#xff0c;接下来主要介绍它们都可以用做什么。 从上图不难看出开发板引脚也不是有多少&#xff0c;分类来看也就以下种类型&…

C语言、Eazy_X——五子棋

//五子棋#include<graphics.h>#define board_size 20 #define pixel 600 int pr pixel / board_size; char board_data[board_size][board_size]; char current_piece o; int count 0;//检测指定玩家是否获胜 bool CheckWin(char c) {int i, j;//检查行for (i 0; i &…

位运算(6)_只出现一次的数字 II

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 位运算(6)_只出现一次的数字 II 收录于专栏【经典算法练习】 本专栏旨在分享学习算法的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 …