【数据结构与算法】——单链表的原理及C语言实现

数据结构与算法——链表原理及C语言实现

  • 链表的原理
  • 链表的基本属性设计
    • 创建一个空链表
    • 链表的遍历(显示数据)
    • 释放链表内存空间
  • 链表的基本操作设计(增删改查)
    • 链表插入节点
    • 链表删除节点
    • 链表查找节点
    • 增删改查测试程序
  • 链表的复杂操作程序设计
    • 单链表的反转
    • 相邻节点最大值
    • 有序链表的合并
    • 链表的排序

参考博文1:【数据结构与算法】程序内功篇三–单链表
参考博文2:链表基础知识详解(非常详细简单易懂)
参考博文3:关于链表,看这一篇就足够了!(新手入门)
参考博文4:单链表——单链表的定义及基本操作

链表的原理

链表含义
  由于顺序表的插入删除操作需要移动大量的元素,影响了运行效率,因此引入了线性表的链式存储——单链表

单链表的特点:

  • 单链表不要求逻辑上相邻的两个元素在物理位置上也相邻,因此链表两个节点的存储空间不相邻。
  • 单链表是非随机的存储结构,即不能直接找到表中某个特定的结点。查找某个特定的结点时,需要从表头开始遍历,依次查找。
  • 对于每个链表结点,分为存放数据的数据域以及存放下个节点地址的地址域

链表节点的定义:

typedef int data_t;//结点定义
typedef struct node{data_t data;		//结点数据域struct node *next;	//结点后继指针域
}listNode;

链表的基本属性设计

创建一个空链表

  通常会用头指针来标识一个单链表,头指针为NULL时表示一个空表。但是,为了操作方便,会在单链表的第一个结点之前附加一个结点,称为头结点。头结点的数据域可以不设任何信息,也可以记录表长等信息。头结点的指针域指向线性表的第一个元素结点。

创建空链表(头指针)主要分为:①申请节点内存空间  ②成员变量赋初始值  ③返回头节点 三大步骤:

  • 功能:创建一个空链表  node为虚拟头节点
  • 参数:void
  • 返回值:头节点地址
/*
功能:创建一个空链表  node为虚拟头节点
参数:void
返回值:头节点地址
*/
listNode* link_create()
{//申请内存空间listNode* node = (listNode *)malloc(sizeof(listNode));if(node == NULL){printf("link_create: malloc error\n");return NULL;}//链表成员变量赋值node->data = 0;node->next = NULL;//返回链表地址return node;
}

链表的遍历(显示数据)

第一步:输出第一个节点的数据域,输出完毕后,让指针保存后一个节点的地址

第二步:输出移动地址对应的节点的数据域,输出完毕后,指针继续后移

第三步:以此类推,直到节点的指针域为NULL

通过遍历链表读取链表的数据并显示:

  • 功能:显示链表数据
  • 参数:para: 链表头
  • 返回值:成功返回0   失败返回-1
/*
功能:显示链表数据
参数:para: 链表头   
返回值:成功返回0;    失败返回-1
*/
int link_show(listNode* head)
{//入口参数检查if(head == NULL)return -1;//遍历链表 while(head->next != NULL){printf("%d ", head->next->data);head = head->next;}printf("\n");return 0;
}

释放链表内存空间

通过遍历链表节点,释放每个节点的内存空间

  • 功能:释放链表内存空间
  • 参数:para: 链表头
  • 返回值: NULL
/*
功能:释放链表内存空间
参数:para :链表头          
返回值: NULL
*/
listNode* link_free(listNode* head)
{//入口参数检查if(head == NULL){printf("list_insert: para error\n");return NULL;}//封装临时节点listNode *temp = head;while(head != NULL){temp = head;//printf("free: %d\n", head->data);//这两行不能反 必须先指向下一个再释放当前地址head = head->next;	free(temp);}return NULL;
}

链表的基本操作设计(增删改查)

链表插入节点

同顺序表一样,向链表中增添元素,根据添加位置不同,可分为以下 3 种情况:

  • 插入到链表的头部(头节点之后),作为首元节点;
  • 插入到链表中间的某个位置;
  • 插入到链表的最末端,作为链表中最后一个数据元素;

虽然新元素的插入位置不固定,但是链表插入元素的思想是固定的,只需做以下两步操作,即可将新元素插入到指定的位置:

  • 将新结点的 next 指针指向插入位置后的结点;
  • 将插入位置前结点的 next 指针指向插入结点;

例如,我们在链表{1,2,3,4}的基础上分别实现在头部、中间部位、尾部插入新元素 5,其实现过程如下图 所示:

头插法程序设计:

  • 功能:链表头插法插入数据
  • 参数:para1:链表头    para2:插入的数据
  • 返回值:成功返回0;   失败返回-1
    /*
    功能:链表头插法插入数据
    参数:para1: 链表头  	para2: 插入的数据
    返回值:成功返回0;    失败返回-1
    */
    int link_push_front(listNode* head, data_t value)
    {//入口参数检查if(head == NULL)return -1;//封装节点listNode* node = (listNode *)malloc(sizeof(listNode));if(node == NULL){printf("link_push_front malloc error\n");return -1;}//头插法node->data = value;			//插入行节点数据node->next = head->next;	//将新节点连接到原来的头head->next = node;			//更新链表头return 0;
    }
    

任意位置插入元素程序设计:

  • 功能:在链表特定位置插入一个元素
  • 参数: para1:链表头    para2:插入的元素值    para3:特定位置的索引
  • 返回值: 失败返回-1    成功返回0
    /*
    功能:在链表特定位置插入一个元素
    参数:	para1:链表头	para2:插入的元素值	para3:特定位置的索引
    返回值: 失败返回-1    成功返回0
    */
    int list_insert(listNode* head, data_t value, int index)
    {//入口参数检查if(head == NULL || index < 0){printf("list_insert: para error\n");return -1;}//封装节点listNode* node = (listNode *)malloc(sizeof(listNode));if(node == NULL){printf("link_push_front malloc error\n");return -1;}node->data = value;//遍历到目标索引处int pos = 0;while(pos < index && head->next != NULL){pos++; head = head->next;}//索引过大if(head->next == NULL && index - pos > 0){printf("index invalid\n");return -1;}//插入数据node->next = head->next;head->next = node;return 0;
    }
    

尾插法程序设计:

  • 功能:链表尾插法插入数据
  • 参数:para1: 链表头    para2:插入的数据
  • 返回值:成功返回0;     失败返回-1
    /*
    功能:链表尾插法插入数据
    参数:para1: 链表头    para2: 插入的数据
    返回值:成功返回0;     失败返回-1
    */
    int link_push_back(listNode* head, data_t value)
    {//入口参数检查if(head == NULL)return -1;//封装节点listNode *node = (listNode *)malloc(sizeof(listNode));if(node == NULL){printf("link_push_back malloc error\n");return -1;}//新节点赋值node->data = value;node->next = NULL;//遍历链表 while(head->next != NULL){head = head->next;}//尾插入节点head->next = node;return 0;
    }
    

链表删除节点

  从链表中删除指定数据元素时,实则就是将存有该数据元素的节点从链表中摘除,但作为一名合格的程序员,要对存储空间负责,对不再利用的存储空间要及时释放。因此,从链表中删除数据元素需要进行以下 2 步操作:

  • 将结点从链表中摘下来;
  • 手动释放掉结点,回收被结点占用的存储空间;

其中,从链表上摘除某节点的实现非常简单,只需找到该节点的直接前驱节点 temp,执行一行程序:

temp->next = temp->next->next;

根据数据值删除某个节点

  • 功能:根据数据值 删除某个节点
  • 参数: para1:链表头  para2:删除数值
  • 返回值: 失败返回-1  成功返回0
    /*
    功能:根据数据值 删除某个节点
    参数:	para1:链表头	para2:删除数值
    返回值: 失败返回-1    成功返回0
    */
    int list_delete_val(listNode* head, int val)
    {//入口参数检查if(head == NULL){printf("list_insert: para error\n");return -1;}//遍历链表while(head->next != NULL && head->next->data != val){head = head->next;}//链表中无该数据if(head->next == NULL && head->data != val){printf("no such value\n");return -1;	}listNode *temp = head->next;	//暂存需释放空间的节点head->next = head->next->next;	//跳跃拉链,即删除了中间节点free(temp);						//释放节点temp = NULL;					//避免野指针return 0;	
    }
    

根据索引删除某个节点

  • 功能:根据索引 删除某个节点 (链表其实没有所谓的索引,即第几个节点-1)
  • 参数: para1:链表头   para2:删除的索引
  • 返回值: 失败返回-1    返回 0
    /*
    功能:根据索引 删除某个节点 (链表其实没有所谓的索引,即第几个节点-1)
    参数:	para1:链表头	para2:删除的索引
    返回值: 失败返回-1    成功返回0
    */
    int link_delete_index(listNode* head, int index)
    {//入口参数检查if(head == NULL || index < 0){printf("link_delete_index: para error\n");return -1;}//遍历链表int pos = 0;while(pos < index && head != NULL){//节点遍历完了if(head->next == NULL){	printf("index error\n");return -1;}pos++;					//索引head = head->next;		//指针偏移}//printf("data: %d\n", head->data);//判断后一个元素是否为空if(head->next == NULL){printf("index error\n");return -1;}listNode *temp = head->next;	//暂存需释放空间的节点head->next = head->next->next;	//跳跃拉链,即删除了中间节点free(temp);						//释放节点temp = NULL;					//避免野指针return 0;	
    }
    

链表查找节点

按数值查找:
  查找数据value在单链表link中的节点索引。
  算法思想:从单链表的第一个结点开始,依次比较表中各个结点的数据域的值,若某结点数据域的值等于value,则返回该节点的索引;若整个单链表中没有这样的结点,则返回-1。

  • 功能:查找某个元素的下标索引
  • 参数:para1:链表头 para2:查找的某个元素值
  • 返回值: 失败返回-1  成功返回元素的下标索引
/*
功能:查找某个元素的下标索引
参数:para1:链表头	para2:查找的某个元素值
返回值: 失败返回-1    成功返回元素的下标索引
*/
int list_search(listNode* head, data_t val)
{//入口参数检查if(head == NULL){printf("list_insert: para error\n");return -1;}//pos 记录下标位置int pos = -1;while(head->next != NULL){pos++;if(head->next->data == val)	//判断是否存在valreturn pos;head = head->next;}return -1;
}

增删改查测试程序

//-------------测试程序-------------
void link_test()
{listNode *head = link_create();int data1[] = {-3, 2, 9, 5, 101};int data2[] = {4, 100, 0};//尾插法插入数据for( int i = 0; i < 5; i++)link_push_back(head, data1[i]);//头插法插入数据for( int i = 0; i < 3; i++)	link_push_front(head, data2[i]);//特定位置插入数据link_insert(head,66,1);//显示原链表printf("src link:");link_show(head);link_delete_index(head, 2);	//删除下标为2的节点link_delete_val(head, 66);	//删除数据为66的节点printf("delete:  ");link_show(head);//查找int ret = link_getVal( head, 2);int idx = link_search( head, 101);printf("data[2]: %d\n", ret);printf("101's index: %d\n", idx);head = link_free(head);	
}

链表的复杂操作程序设计

单链表的反转

算法思路: 依次取原链表中各结点,将其作为新链表首结点插入head结点
即获取原链表的每个节点,在新链表进行头插法插入:

  1. 判断是否为空 / 是否只有1个节点
  2. 断开链表,一分为二,分为第一个节点和后面链表
  3. 遍历从第二个后面起的节点,头插法循环插入

  • 功能:单链表的反转
  • 参数: para:   链表头
  • 返回值: 失败返回-1   成功返回0
int link_reverse(listNode* head)
{//入口参数检查if(head == NULL ){printf("head is NULL.\n");return -1;}//只有一个节点if(head->next == NULL || head -> next ->next == NULL ){return 0;}//p指向待操作的节点listNode *p = head->next->next;	//新链表头head->next->next = NULL;		//链表一分为二listNode *q = p;//遍历后续节点,以尾插法插入新的链表while(p != NULL){q = p;p = p->next;//头插法 插入qq->next = head->next;head->next = q;}return 0;
}

测试程序:

void link_reverse_test()
{listNode *head = link_create();int data1[] = {-3, 2, 9, 5, 3};for(int i = 0; i < 5; i++)link_push_back(head, data1[i]);//打印原链表printf("link: ");link_show(head);//翻转链表link_reverse(head);//打印翻转后的链表printf("reverse: ");link_show(head);//释放内存空间head = link_free(head);
}

相邻节点最大值

算法思路: 设q,p分别为链表中相邻两结点指针,其中p在前,q在后,求q->data + q->data为最大的那一组值,返回其相应的指针q即可:

  1. 节点个数 <= 2,退出
  2. 初始化辅助变量(遍历节点指针p,q(p在前,q在后),两数之和sum,新节点指针ret指向head,并以头节点一分为二)
  3. 遍历p和q,以及sum

  • 功能:求相邻节点最大值,返回最大值第一个节点地址,最大值通过参数传递
  • 参数: para1:链表头 para2:最大和的值的地址
  • 返回值: 失败返回NULL 成功返回第一个节点指针
//求链表中相邻两节点data值之和为最大的第一节点的指针
/*
功能:求相邻节点最大值,返回最大值第一个节点地址
最大值通过参数传递
参数:	para1:链表头 	para2:最大和的值的地址
返回值: 失败返回NULL  成功返回第一个节点指针
*/
listNode *list_adjmax(listNode *head, data_t *value)
{if(head == NULL){printf("head is NULL\n");return NULL;}if(head->next == NULL || head->next->next == NULL || head->next->next->next == NULL)return head;//构造辅助变量listNode *ret = head->next;			//结果链表尾指针listNode *p   = head->next->next;	//遍历链表的指针, p在前面listNode *q   = head->next;			//遍历链表的指针, q在后data_t max = q->data + p->data ;	//初始化两数之和while(p->next != NULL){//p q更新p = p->next;q = q->next;//比较最大值if(q->data + p->data > max){max = q->data + p->data;	//更新最大值ret = q;					//更新最大值第一个节点}}//返回相邻最大值的第一个节点指针,并通过参数传回最大值*value = max;return ret;
}

测试程序:

void list_adjmax_test()
{listNode *head = link_create();int data1[] = {-3, 2, 9, 5, 3};for(int i = 0; i < 5; i++)link_push_back(head, data1[i]);//打印原链表printf("link: ");link_show(head);//计算最大两数之和及第一个节点int sum;listNode *ret = list_adjmax(head, &sum);//打印新节点数及其两数最大之和printf("data: %d\nsum: %d", ret->data, sum);//释放内存空间head = link_free(head);	
}

有序链表的合并

算法思想: 设指针p、q分别指向表A和B中的结点,若p -> data <= q -> data,则p进入结果表,否则q结点进入结果表。

  1. 参数判断(链表是否为空)
  2. 分别将链表一分为二,p,q分别指向两个链表的新的头,结果存放在以ret为尾节点的链表中
  3. 通过p,q指针遍历两个链表,将小的数值插入到ret结果链表中形成新的合并链表
  • 功能:合并两个链表,合并至head1
  • 参数: para1: 链表1头节点  para2: 链表2头节点
  • 返回值: 失败返回 -1  成功返回 0
//合并两个有序链表
/*
功能:合并两个链表,合并至head1
参数:	para1:链表1头节点	para2:链表2头节点
返回值: 失败返回-1  成功返回0
*/
int link_merge(listNode *head1, listNode *head2)
{//入口参数检查if( head1 == NULL || head2 == NULL){printf("head1 || head2 error\n");return -1;}//变量初始化listNode *p = head1->next;listNode *q = head2->next;listNode *ret = head1;head1->next = NULL;head2->next = NULL;while(p != NULL && q != NULL){if(p->data <= q->data){ret->next = p;		//p接入ret链表p = p->next;		//更新pret = ret->next;	//更新新表尾ret->next = NULL;	//置空新表尾}else{ret->next = q;		//q接入ret链表q = q->next;		//更新qret = ret->next;	//更新新表尾ret->next = NULL;	//置空新表尾}}//把多的p或q接入到ret链表if( p != NULL)ret->next = p;if( q != NULL)ret->next = q;return 0;
}

测试程序:

void link_merge_test()
{listNode *head1 = link_create();listNode *head2 = link_create();int data1[] = {1, 2, 4, 6, 8};int data2[] = {2, 5, 6, 22, 96, 128};//插入数据for(int i = 0; i < 5; i++)link_push_back(head1, data1[i]);for(int i = 0; i < 6; i++)link_push_back(head2, data2[i]);	//打印原有序链表printf("link1: ");link_show(head1);printf("link2: ");link_show(head2);//合并printf("merge: ");link_merge(head1, head2);link_show(head1);//去重printf("purge: ");link_purge(head1);link_show(head1);head1 = link_free(head1);	head2 = link_free(head2);	
}

链表的排序

  • 如果链表为空或只有一个结点,不需要排序
  • 先将第一个结点与后面所有的结点依次对比数据域,只要有比第一个结点数据域小的,则交换位置 ,交换之后,拿新的第一个结点的数据域与下一个结点再次对比,如果比他小,再次交换,以此类推
  • 第一个结点确定完毕之后,接下来再将第二个结点与后面所有的结点对比,直到最后一个结点也对比完毕为止
int link_sort(listNode *head )
{//头节点为空if (head == NULL){printf("head is NULL\n");return -1;}//只有1个节点if (head->next == NULL){printf("only one node\n");return 0;}listNode *q, *p, temp;p = head->next;			//从第一个节点开始while (p->next != NULL){q = p->next;		//q从基准元素的下个元素开始while (q != NULL){if (p->data > q->data)	//后面的元素小{//交换值temp = *q;			*q = *p;*p = temp;//交换地址temp.next = q->next;q->next = p->next;p->next = temp.next;}q = q->next;}p = p->next;}return 0;
}

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

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

相关文章

Vulnhub billu b0x

0x01 环境搭建 1. 从官方下载靶机环境&#xff0c;解压到本地&#xff0c;双击OVF文件直接导入到vmware虚拟机里面。2. 将虚拟机的网络适配器调成NAT模式&#xff0c;然后开机即可进行操作了。 0x02 主机发现 nmap -sn 192.168.2.0/24 成功获取靶机IP为192.168.2.129。 0x0…

本次安装Visual Studio 所用的安装程序不完整。请重新运行VisualStudio安装程序以解决此问题

今天点开VS的时候遇到了这个问题 因为昨天升级到一半电脑关机了&#xff0c;今天打开软件遇到如下错误&#xff0c; 解决办法很简单&#xff0c;找到安装目录进入Installer文件夹 我的目录在C:\Program Files (x86)\Microsoft Visual Studio\Installer 找到vs_installer.exe…

【python】python爱心代码

一、实现效果&#xff1a; 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 二、准备工作 &#xff08;1)、导入必要的模块&#xff1a; 代码首先导入了需要使用的模块&#xff1a;requests、lxml和csv。 import requests from lxml import etree import csv 如果出现…

C#写个小工具,把多个word文档进行合并成一个word文档

先要安装包 帮助类WordDocumentMerger&#xff0c;用于处理word合并功能 using System; using System.Collections.Generic; using System.Text; using Microsoft.Office.Interop.Word; using System.Reflection; using System.IO; using System.Diagnostics;namespace WordH…

分别用JavaScript,Java,PHP,C++实现桶排序的算法(附带源码)

桶排序是计数排序的升级版。它利用了函数的映射关系&#xff0c;高效与否的关键就在于这个映射函数的确定。为了使桶排序更加高效&#xff0c;我们需要做到这两点&#xff1a; 在额外空间充足的情况下&#xff0c;尽量增大桶的数量使用的映射函数能够将输入的 N 个数据均匀的分…

【C语言】字符串函数介绍

目录 前言&#xff1a; 1. strlen 函数 函数介绍 strlen 函数的使用 strlen 函数的模拟实现 2. strcpy 函数 函数介绍 strcpy 函数的使用 strcpy 函数的模拟实现 3. strcat 函数 函数介绍 strcat 函数的使用 strcat 函数的模拟实现 4. strcmp 函数 函数介绍 st…

4K Video Downloader forMac/win:畅享高清视频下载的终极利器!

在如今的数字时代&#xff0c;高清视频已经成为人们生活中不可或缺的一部分。无论是观看精彩的电影、音乐视频&#xff0c;还是学习教育类的在线课程&#xff0c;我们都希望能够以最清晰流畅的方式来欣赏。而为了满足这一需求&#xff0c;我们需要一款功能强大的高清视频下载软…

工业平板电脑定制_三防平板电脑安卓主板厂家

工业平板电脑具有IP68级三防品质&#xff0c;采用高强度工业材质制造&#xff0c;结构稳固坚韧&#xff0c;具备较高的抗冲击和防震能力。隔空减震技术进一步加强了产品的抗冲击和防震动功能。广泛应用于工控、医疗、电信、电力、工业自动化设备、汽车检测、制造业等多个领域&a…

Flink实时数仓同步:快照表实战详解

一、背景 在大数据领域&#xff0c;初始阶段业务数据通常被存储于关系型数据库&#xff0c;如MySQL。然而&#xff0c;为满足日常分析和报表等需求&#xff0c;大数据平台采用多种同步方式&#xff0c;以适应这些业务数据的不同存储需求。这些同步存储方式包括离线仓库和实时仓…

MySQL数据库入门

MySQL数据库概述 1&#xff0c;为什么要使用数据库2&#xff0c;数据库的相关概念3&#xff0c;常见的数据库管理系统4&#xff0c;MySQL介绍5&#xff0c;关系型数据库和非关系型数据库6&#xff0c;关系型数据库的设计规则7&#xff0c;表的关联关系7.1&#xff0c;一对一7.2…

短剧小程序开发:打造高效、便捷的娱乐体验

随着移动互联网的普及和用户需求的多样化&#xff0c;短剧小程序作为一种新型的应用形态&#xff0c;逐渐受到了广大用户的青睐。短剧小程序开发旨在为用户提供一种高效、便捷的娱乐体验&#xff0c;让用户在忙碌的生活中轻松享受到精彩的短剧内容。本文将探讨短剧小程序开发的…

0203-2-输入输出系统

第六章&#xff1a;输入输出系统 I/O系统的功能&#xff0c;模型和接口 I/O系统管理的对象是I/O设备和相应的设备控制器。 I/O系统的基本功能 隐藏物理设备的细节与设备的无关性提高处理机和I/O设备的利用率对I/O设备进行控制确保对设备的正确共享错误处理 I/O软件的层次结…

Vite与Webpack打包内存溢出问题优雅处理方式

Vite与Webpack打包内存溢出问题处理 文章目录 Vite与Webpack打包内存溢出问题处理1. Vite1. 打包错误提示2. 命令行方式解决3. 配置环境变量方式解决1. 设置变量2. 配置系统的环境变量 2. Webpack1. 打包错误提示2. 命令行方式解决3. 配置环境变量方式解决1. 设置变量2. 配置系…

【DDD】学习笔记-什么是模型

从领域驱动的战略设计进入战术设计&#xff0c;简单说来&#xff0c;就是跨过系统视角的限界上下文边界进入它的内部&#xff0c;从分层架构的逻辑分层进入到每一层的内部。在思考内部的设计细节时&#xff0c;首先需要思考的问题就是&#xff1a;什么是模型&#xff08;Model&…

NETX90-多协议通讯芯片

随着作为信息物理系统核心技术的工业物联网的发展&#xff0c;Hilscher 基于 netX 51/52成功开发了新一代网络控制器netX90&#xff0c;其安全性是产品的核心价值。可实现更高性能的集成&#xff0c;并提高功率效率等级&#xff0c;凭借其较小的外形尺寸能够满足规格尺寸更小的…

css新手教程

css新手教程 课程&#xff1a;14、盒子模型及边框使用_哔哩哔哩_bilibili 一.什么是CSS 1.什么是CSS Cascading Style Sheet 层叠样式表。 CSS&#xff1a;表现&#xff08;美化网页&#xff09; 字体&#xff0c;颜色&#xff0c;边距&#xff0c;高度&#xff0c;宽度&am…

Linux信号详解~

目录 前言 一、初识信号 二、信号的概念 三、信号的发送与捕捉 3.1 信号的发送 3.1.1 kill 命令 3.1.2 kill 函数 3.1.3 raise函数 3.1.4 abort函数 3.2 信号的捕捉 3.2.1 signal函数 3.2.2 sigaction函数 3.2.3 图示 四、信号的产生 4.1 硬件异常产生信号 4.2 …

CMake Msys2 搭配vscode

(一)MSYS2介绍 MSYS2&#xff08;Minimal SYStem 2&#xff09;是一个集成了大量的GNU工具链、工具和库的开源软件包集合。它提供了一个类似于Linux的shell环境&#xff0c;可以在Windows系统中编译和运行许多Linux应用程序和工具。 MSYS2基于MinGW-w64平台&#xff0c;提供了…

Linux---进程间通信 | 管道 | PIPE | MKFIFO | 共享内存 | 消息队列

管道 管道是UNIX中最古老的进程间通信的形式&#xff0c;我们把从一个进程连接到另一个进程的数据流称为一个管道。 一个文件&#xff0c;可以被多个进程打开吗&#xff1f;可以&#xff0c;那如果一个进程打开文件&#xff0c;往文件里面写数据&#xff0c;另一个进程打开文…