C——单链表

一.前言

我们在前面已经了解了链表中的双向链表,而我们在介绍链表分类的时候就说过常用的链表只有两种——双向带头循环链表和单向不带头不循环链表。下来我来介绍另一种常用的链表——单向不带头不循环链表也叫做单链表。不清楚链表分类的以及不了解双向链表的可以看我之前的博客C——双向链表-CSDN博客。

二.单链表的结构

我们已经了解了单链表的全称叫做单向不带头不循环链表,我们怎么理解这链表前面的修饰词呢?其实,单链表就像是一节节连接起来的火车车厢一样。但是这节车厢只可以向后走,并且火车的车头和车尾是不能连接在一起的。

我们照着这张图再来分析单链表的结构:单向的意思就是只能沿着D->A->C->E->Z->X->W这个方向走,那不带头的意思就是没有头节点即哨兵位,我们在讲双向链表的时候提到了哨兵位,他只是一个没有有效数据的头节点。不循环的意思我们也可以对照这双向链表来看,双向链表的尾节点可以找到头节点,头节点也可以找到尾节点,这就是循环;而单链表的尾节点指向的不是头节点而是NULL,头节点也不能找到尾节点。

那么单链表的节点到底是什么样的呢?

 我们再来与双向链表的节点进行对比一下:

 单链表节点只有两个成员,分别是数据和指向下一个节点的指针,而双向链表的节点有三个成员,分别是数据,指向前一个节点的指针和指向下一个节点的指针。

综上所述:单链表是一个只能指向下一个节点的链表,而且没有头节点,并且是不循环的。 

三.实现单链表

与双向链表的实现相似,实现单链表也需要很多的函数及头文件,所以我们将所有的函数声明和头文件都放到singlelist.h中,函数的实现都放入singlelist.c中。

3.1单链表节点的结构

//链表节点
struct SingleList
{
	int val;
	struct SingleList* next;
};

我们这样写虽然可以完成链表的基本结构,但是难道我们链表只能存储整形嘛?如果我们创建了很多个整型节点,但是有一天我们想利用链表存储字符型或者浮点型数据,那么我们就得一个一个的去修改,费时费力;还有就是这个节点的名称很长,在后面的代码中会重复出现,也很浪费时间。所以我们可以利用typedef关键词,对节点重命名,以及对int重命名。

//节点数据类型
typedef int SingleListdatatype;

//链表节点
typedef struct SingleListNode
{
	SingleListdatatype val;
	struct SingleListNode* next;
}SLNode;

这样我们在多次使用该类型的时候就不需要再写那么长一串了,以后再修改储存的数据类型的时候也就不用一个一个节点的修改了,只需将定义的节点数据类型中的数据类型修改即可。

3.2单链表节点的创建

 我们在创建节点的时候实际上就是创建一个结构体变量,我们可以利用动态内存管理为我们的每一个节点动态开辟一块内存空间。

//节点的创建
SLNode* BuyNode(SingleListdatatype x)
{
	SLNode* node = (SLNode*)malloc(sizeof(SLNode));
	if (node == NULL)
	{
		perror("malloc");
		exit(-1);
	}
	node->val = x;
	node->next = NULL;
	return node;
}

 我们来测试一下节点创建函数是否正确:

我们利用此测试代码来调试发现,我们创建了4个节点,通过调试也确实观察到了这四个节点,并且它们储蓄的数据就是我们传的数据,指向下一个节点的指针也是NULL。与我们想要实现的结果相同。 

那我们现在将这四个节点连接起来:

void test1()
{
	SLNode* plist = NULL;
	SLNode* plist1 = BuyNode(1);
	SLNode* plist2 = BuyNode(2);
	SLNode* plist3 = BuyNode(3);
	SLNode* plist4 = BuyNode(4);

	plist1->next = plist2;
	plist2->next = plist3;
	plist3->next = plist4;
}

int main()
{
	test1();
	return 0;
}

我们再来调试一下: 

我们看到将所有节点连接起来之后,我们就可以利用第一个节点找到后面的节点。 

3.3链表的打印

//链表的打印
void SLNodePrint(SLNode* plist)
{
	while (plist)//plist != NULL
	{
		printf("%d->", plist->val);
		plist = plist->next;
	}
	printf("NULL\n");
}

 我们再来测试一下该函数:

也是没有出现问题,说明我们链表的打印已经完成了。 

3.4尾插

我们现在创建的节点需要我们手动将其连接起来,我们可以利用尾插方法,将新创建的节点直接连接到前一个节点上。 

//尾插
void SLNodepushback(SLNode** plist, SingleListdatatype x)
{
	assert(plist);

	SLNode* newnode = BuyNode(x);//新节点

	if (*plist == NULL)
	{
		//空链表
		*plist = newnode;
	}
	else
	{
		//非空链表
		//遍历原链表,找到尾节点
		SLNode* pcur = *plist;
		while (pcur->next)//(*plist)->next != NULL
		{
			pcur = pcur->next;
		}
		pcur->next = newnode;
	}
}

我们测试下尾插方法:

我们尾插了四次,通过打印方法,将链表打印出来,说明我们的尾插方法也没有问题。但是有些人可能会有疑问:为什么这里传的是二级指针 而不传一级指针?

因为我们是通过一个结构体类型的指针来维护我们的链表,而我们传一级指针的话相当于值传递,而对于值传递来说,形参改变是不影响实参的。简单来说,就说你在函数内部将形参节点的next指针改变了,但是却不影响实参的值。达不到我们的目的。所以要想形参的改变影响实参的话,我们就得地址传递,就得传一级指针的地址,也就是二级指针

3.5头插 

头插从名字就可以听出来是把一个新节点插到链表的头部,我们利用图来分析如何进行头插:

//头插
void SLpushfront(SLNode** plist, SingleListdatatype x)
{
	assert(plist);

	SLNode* newnode = BuyNode(x);
	newnode->next = *plist;
	*plist = newnode;
}

我们测试头插方法:

根据打印结果来看,头插的方法也没有任何问题。 

3.6尾删

尾删就是删除链表中的尾节点,那么前提就是该链表不能为空,如果都是空链表了,怎么执行删除操作呢?我们画图来分析一下:

//尾删
void SLpopback(SLNode** plist)
{
	assert(plist && *plist);//*plist != NULL防止链表为空

	SLNode* pcur = *plist;//记录倒数第二个节点
	SLNode* del = *plist;//记录尾节点
	while (del->next)
	{
		pcur = del;
		del = del->next;
	}
	pcur->next = NULL;
	free(del);
	del = NULL;
}

我们测试之后发现出了错误,这是为什么呢?

我们进行四次尾删,最后一次删除会将1删除掉,此时应该只打印NULL,这里打印了随机值。这是为什么呢? 

//尾删
void SLpopback(SLNode** plist)
{
	assert(plist && *plist);//*plist != NULL防止链表为空

	if (!((*plist)->next))//(*plist)->next == NULL
	{
		//链表只有一个节点
		free(*plist);
		*plist = NULL;
	}
	else
	{
		//链表有多个节点
		SLNode* pcur = *plist;//记录倒数第二个节点
		SLNode* del = *plist;//记录尾节点
		while (del->next)
		{
			pcur = del;
			del = del->next;
		}
		pcur->next = NULL;
		free(del);
		del = NULL;
	}
}

我们现在再来测试一下修改后的尾删代码: 

我们看到,现在就没有问题了。

3.7头删

我们实现了尾删,现在来实现头删。头删的前提与尾删的前提相同:链表不能为空。头删实际上就是删除链表中的第一个节点,我们画图分析如何实现头删:

//头删
void SLpopfront(SLNode** plist)
{
	assert(plist && *plist);//链表不能为空

	SLNode* next = (*plist)->next;
	free(*plist);
	*plist = next;
}

我们来测试头删方法:

没有问题~ 

那如果我们多删一次呢?

我们看到,第五次删除的时候这时候已经是空链表了,我们再看提示的错误信息,说是断言出错了,这就说明了链表为空了。 

3.8查找数据

查找数据非常简单,我们只需要遍历链表,将要查找的数据与链表中的数据进行对比,如果找到了则返回该数据对应节点的地址;如果没找到则返回NULL。

//查找
SLNode* SLFind(SLNode* plist, SingleListdatatype x)
{
	assert(plist);

	SLNode* pcur = plist;
	while (pcur)
	{
		if (pcur->val == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}

测试查找:

结果没毛病,老铁OK了! 

3.9在指定位置之前插入数据

我们要在指定位置的前面插入数据,所以我们必须得找到该指定位置,这就需要调用我们的查找方法了。我们来画图分析一下:

//在指定位置之前插入数据
void SLposfront(SLNode** plist, SLNode* pos, SingleListdatatype x)
{
	assert(plist && *plist);//链表不能为空,否则找不到指定位置

	if (pos == *plist)
	{
		//pos节点就是第一个节点
		//调用头插方法
		SLpushfront(plist,x);
	}
	else
	{
		//pos节点不是第一个节点
		SLNode* newnode = BuyNode(x);
		SLNode* pcur = *plist;
		SLNode* prev = *plist;
		while (pcur)
		{
			if (pcur->val == pos->val)
			{
				break;
			}
			prev = pcur;
			pcur = pcur->next;
		}
		// prev newnode pcur/pos
		prev->next = newnode;
		newnode->next = pcur;
	}
}

 我们测试该代码:

我们测试了三种位置的插入,无论是第一个节点之前还是中间节点之前或者是尾节点之前都没有问题。

3.10在指定位置之后插入数据

在指定位置之后插入数据与在指定位置之前插入数据有相同点但也有不同点,我们继续来画图分析:

//在指定位置之后插入数据
void SLposback(SLNode* pos, SingleListdatatype x)
{
	assert(pos);
	SLNode* newnode = BuyNode(x);
	//pos newnode pos->next
	newnode->next = pos->next;
	pos->next = newnode;
}

我们对其进行测试来看: 

3.11删除pos节点

我们现在来删除指定节点,前提肯定是链表不能为空。我们接着来画图分析:

//删除pos节点
void slpoppos(SLNode** plist, SLNode* pos)
{
	assert(plist && *plist);
	assert(pos);

	if (*plist == pos)
	{
		//pos是头节点,调用头删方法
		SLpopfront(plist);
	}
	else
	{
		SLNode* prev = *plist;
		while (prev->next != pos)
		{
			//找到pos的前一个节点
			prev = prev->next;
		}
		//prev pos pos->next
		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
}

测试该代码: 老铁没毛病!

3.12删除pos节点之后的节点

删除pos节点之后的节点我们要改变指向的指针就只有一个了,那就是pos->next指针。要让它指向pos->next->next。我们画图分析:

//删除pos节点后一个节点
void slpopposback(SLNode* pos)
{
	assert(pos && pos->next);//pos->next != NULL避免pos是尾节点

	SLNode* Next = pos->next;
	pos->next = pos->next->next;
	free(Next);
	Next = NULL;
}

 测试代码:

那如果我们的pos节点就是尾节点程序会发生什么呢? 

我们看到,上面提醒我们断言出错,说明此时pos节点为尾节点。 

3.13销毁链表

//销毁链表
void DestorySL(SLNode** plist)
{
	assert(plist && *plist);
	SLNode* Next = (*plist)->next;
	while (Next)
	{
		free(*plist);
		*plist = Next;
		Next = Next->next;
	}
	free(*plist);
	*plist = NULL;
}

 我们通过调试来判断我们的销毁方法:没有问题!

到这里,我们单链表的所有方法都已经完成了。下面附上完整代码:

四.完整代码

1.singlelist.h

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

//节点数据类型
typedef int SingleListdatatype;

//链表节点
typedef struct SingleListNode
{
	SingleListdatatype val;
	struct SingleListNode* next;
}SLNode;

//节点的创建
SLNode* BuyNode(SingleListdatatype x);

//链表的打印
void SLNodePrint(SLNode* plist);

//尾插
void SLNodepushback(SLNode** plist, SingleListdatatype x);

//头插
void SLpushfront(SLNode** plist, SingleListdatatype x);

//尾删
void SLpopback(SLNode** plist);

//头删
void SLpopfront(SLNode** plist);

//查找
SLNode* SLFind(SLNode* plist, SingleListdatatype x);

//在指定位置之前插入数据
void SLposfront(SLNode** plist, SLNode* pos,SingleListdatatype x);

//在指定位置之后插入数据
void SLposback(SLNode* pos, SingleListdatatype x);

//删除pos节点
void slpoppos(SLNode** plist, SLNode* pos);

//删除pos节点后一个节点
void slpopposback(SLNode* pos);

//销毁链表
void DestorySL(SLNode** plist);

2.singlelist.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "SingleList.h"

//节点的创建
SLNode* BuyNode(SingleListdatatype x)
{
	SLNode* node = (SLNode*)malloc(sizeof(SLNode));
	if (node == NULL)
	{
		perror("malloc");
		exit(-1);
	}
	node->val = x;
	node->next = NULL;
	return node;
}

//链表的打印
void SLNodePrint(SLNode* plist)
{
	while (plist)//plist != NULL
	{
		printf("%d->", plist->val);
		plist = plist->next;
	}
	printf("NULL\n");
}

//尾插
void SLNodepushback(SLNode** plist, SingleListdatatype x)
{
	assert(plist);

	SLNode* newnode = BuyNode(x);//新节点

	if (*plist == NULL)
	{
		//空链表
		*plist = newnode;
	}
	else
	{
		//非空链表
		//遍历原链表,找到尾节点
		SLNode* pcur = *plist;
		while (pcur->next)//(*plist)->next != NULL
		{
			pcur = pcur->next;
		}
		pcur->next = newnode;
	}
}

//头插
void SLpushfront(SLNode** plist, SingleListdatatype x)
{
	assert(plist);

	SLNode* newnode = BuyNode(x);
	newnode->next = *plist;
	*plist = newnode;
}

//尾删
void SLpopback(SLNode** plist)
{
	assert(plist && *plist);//*plist != NULL防止链表为空

	if (!((*plist)->next))//(*plist)->next == NULL
	{
		//链表只有一个节点
		free(*plist);
		*plist = NULL;
	}
	else
	{
		//链表有多个节点
		SLNode* pcur = *plist;//记录倒数第二个节点
		SLNode* del = *plist;//记录尾节点
		while (del->next)
		{
			pcur = del;
			del = del->next;
		}
		pcur->next = NULL;
		free(del);
		del = NULL;
	}
}

//头删
void SLpopfront(SLNode** plist)
{
	assert(plist && *plist);//链表不能为空

	SLNode* next = (*plist)->next;
	free(*plist);
	*plist = next;
}

//查找
SLNode* SLFind(SLNode* plist, SingleListdatatype x)
{
	assert(plist);

	SLNode* pcur = plist;
	while (pcur)
	{
		if (pcur->val == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}

//在指定位置之前插入数据
void SLposfront(SLNode** plist, SLNode* pos, SingleListdatatype x)
{
	assert(plist && *plist);//链表不能为空,否则找不到指定位置
	assert(pos);

	if (pos == *plist)
	{
		//pos节点就是第一个节点
		//调用头插方法
		SLpushfront(plist,x);
	}
	else
	{
		//pos节点不是第一个节点
		SLNode* newnode = BuyNode(x);
		SLNode* pcur = *plist;
		SLNode* prev = *plist;
		while (pcur)
		{
			if (pcur->val == pos->val)
			{
				break;
			}
			prev = pcur;
			pcur = pcur->next;
		}
		// prev newnode pcur/pos
		prev->next = newnode;
		newnode->next = pcur;
	}
}

//在指定位置之后插入数据
void SLposback(SLNode* pos, SingleListdatatype x)
{
	assert(pos);
	SLNode* newnode = BuyNode(x);
	//pos newnode pos->next
	newnode->next = pos->next;
	pos->next = newnode;
}

//删除pos节点
void slpoppos(SLNode** plist, SLNode* pos)
{
	assert(plist && *plist);
	assert(pos);

	if (*plist == pos)
	{
		//pos是头节点,调用头删方法
		SLpopfront(plist);
	}
	else
	{
		SLNode* prev = *plist;
		while (prev->next != pos)
		{
			//找到pos的前一个节点
			prev = prev->next;
		}
		//prev pos pos->next
		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
}

//删除pos节点后一个节点
void slpopposback(SLNode* pos)
{
	assert(pos && pos->next);//pos->next != NULL避免pos是尾节点

	SLNode* Next = pos->next;
	pos->next = pos->next->next;
	free(Next);
	Next = NULL;
}

//销毁链表
void DestorySL(SLNode** plist)
{
	assert(plist && *plist);
	SLNode* Next = (*plist)->next;
	while (Next)
	{
		free(*plist);
		*plist = Next;
		Next = Next->next;
	}
	free(*plist);
	*plist = NULL;
}

3.test.c 

#define _CRT_SECURE_NO_WARNINGS 1
#include "SingleList.h"

//void test1()
//{
//	SLNode* plist1 = BuyNode(1);
//	SLNode* plist2 = BuyNode(2);
//	SLNode* plist3 = BuyNode(3);
//	SLNode* plist4 = BuyNode(4);
//
//	plist1->next = plist2;
//	plist2->next = plist3;
//	plist3->next = plist4;
//
//	SLNodePrint(plist1);
//}


void test2()
{
	SLNode* plist = NULL;
	//测试尾插
	SLNodepushback(&plist, 1);
	SLNodepushback(&plist, 2);
	SLNodepushback(&plist, 3);
	SLNodepushback(&plist, 4);
	SLNodePrint(plist);

	//测试销毁链表
	DestorySL(&plist);

	//测试删除pos节点的下一个节点
	//SLNode* find = SLFind(plist, 4);
	//slpopposback(find);
	//SLNodePrint(plist);

	//测试删除pos节点
	//SLNode* find = SLFind(plist, 4);
	//slpoppos(&plist,find);
	//SLNodePrint(plist);


	//测试在指定位置之后插入数据
	//SLNode* find = SLFind(plist, 1);
	//SLposback(find,66);
	//SLNodePrint(plist);

	//测试在指定位置之前插入数据
	//SLNode* find = SLFind(plist, 4);
	//SLposfront(&plist,find,100);
	//SLNodePrint(plist);

	//测试查找
	//SLNode* find = SLFind(plist,99);
	//if (find == NULL)
	//{
	//	printf("找不到!");
	//}
	//else
	//{
	//	printf("找到了!");
	//}

	//测试头删
	//SLpopfront(&plist);
	//SLNodePrint(plist);//2 3 4
	//SLpopfront(&plist);
	//SLNodePrint(plist);//3 4
	//SLpopfront(&plist);
	//SLNodePrint(plist);//4
	//SLpopfront(&plist);
	//SLNodePrint(plist);//NULL
	//SLpopfront(&plist);
	//SLNodePrint(plist);


	//测试尾删
	//SLpopback(&plist);
	//SLNodePrint(plist);//1 2 3
	//SLpopback(&plist);
	//SLNodePrint(plist);//1 2
	//SLpopback(&plist);
	//SLNodePrint(plist);//1
	//SLpopback(&plist);
	//SLNodePrint(plist);//NULL


	//测试头插
	//SLpushfront(&plist,100);
	//SLNodePrint(plist);
	//SLpushfront(&plist,99);
	//SLNodePrint(plist);
}

int main()
{
	//test1();
	test2();
	return 0;
}

完!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

react18【系列实用教程】JSX (2024最新版)

为什么要用 JSX&#xff1f; JSX 给 HTML 赋予了 JS 的编程能力 JSX 的本质 JSX 是 JavaScript 的语法扩展&#xff0c;浏览器本身不能识别&#xff0c;需要通过解析工具&#xff08;如babel&#xff09;解析之后才能在浏览器中运行。 bable 官网可以查看解析过程 JSX 的语法 …

杭州打的样,适合全国推广

房地产 昨天&#xff0c;杭州和西安全面解除房地产限购。 在房价跌跌不休的今天&#xff0c;这两大城市取消限购其实并不意外。 尤其是杭州&#xff0c;土地财政依赖全国第一&#xff0c;绷不住很正常。 近十年&#xff0c;杭州依靠于亚运会、G20 和阿里巴巴&#xff0c;涨得飞…

将机械手与CodeSys中的运动学模型绑定

文章目录 1.背景介绍2.选定运动学模型3.机械手各尺寸的对应4.总结4.1.选择正确的运动学模型4.2.注意各个关节旋转的正方向。4.3.编码器零点与机械零点的偏移修正。 1.背景介绍 最近搞到了一台工业机械手&#xff0c;虽然这个机械手有自己的控制程序&#xff0c;但是我们还是想…

Java入门基础学习笔记1——初识java

1、为什么学习java&#xff1f; 几乎统治了服务端的开发&#xff1b;几乎所有的互联网企业都使用&#xff1b;100%国内大中型企业都用&#xff1b;全球100亿的设备运行java。开发岗位薪资高。 Java的流行度很高&#xff0c;商用占有率很高。 可移植性。 2、Java的背景知识 …

【基础算法总结】二分查找一

二分查找一 1. 二分查找2.在排序数组中查找元素的第一个和最后一个位置3.x 的平方根4.搜索插入位置 点赞&#x1f44d;&#x1f44d;收藏&#x1f31f;&#x1f31f;关注&#x1f496;&#x1f496; 你的支持是对我最大的鼓励&#xff0c;我们一起努力吧!&#x1f603;&#x1…

Java入门基础学习笔记12——变量详解

变量详解&#xff1a; 变量里的数据在计算机中的存储原理。 二进制&#xff1a; 只有0和1&#xff0c; 按照逢2进1的方式表示数据。 十进制转二进制的算法&#xff1a; 除二取余法。 6是110 13是1101 计算机中表示数据的最小单元&#xff1a;一个字节&#xff08;byte&…

【教程向】从零开始创建浏览器插件(三)解决 Chrome 扩展中弹出页面、背景脚本、内容脚本之间通信的问题

第三步&#xff1a;解决 Chrome 扩展中弹出页面、背景脚本、内容脚本之间通信的问题 Chrome 扩展开发中&#xff0c;弹出页面&#xff08;Popup&#xff09;、背景脚本&#xff08;Background Script&#xff09;、内容脚本&#xff08;Content Script&#xff09;各自拥有独立…

word转pdf的java实现(documents4j)

一、多余的话 java实现word转pdf可用的jar包不多&#xff0c;很多都是收费的。最近发现com.documents4j挺好用的&#xff0c;它支持在本机转换&#xff0c;也支持远程服务转换。但它依赖于微软的office。电脑需要安装office才能转换。鉴于没在linux中使用office&#xff0c;本…

hadoop学习---基于Hive的教育平台数据仓库分析案例(二)

衔接第一部分&#xff0c;第一部分请点击&#xff1a;基于Hive的教育平台数据仓库分析案例&#xff08;一&#xff09; 后接第三部分&#xff0c;第三部分请点击&#xff1a;基于Hive的教育平台数据仓库分析案例 (三) 意向用户模块&#xff08;全量分析&#xff09;&#…

用户体验优化uxo指的是什么?

用户体验优化(User Experience Optimization&#xff0c;简称UXO)是一种专注于改善和提升用户在使用企业产品或服务时的整体感受和体验的过程。简单来说&#xff0c;它旨在通过改进产品或服务的设计和功能&#xff0c;使用户在使用过程中感到更加愉悦、满意和高效。用户体验优化…

桌面怎么分类便签 桌面分类便签设置方法

桌面便签&#xff0c;一直是我工作和学习的好帮手。每当灵感闪现或是有待办事项&#xff0c;我都会随手记录在便签上&#xff0c;它们就像我桌面上的小助手&#xff0c;时刻提醒我不要遗漏任何重要事务。 但便签一多&#xff0c;管理就成了问题。一张张五颜六色的便签贴满了我…

C++ 多态的相关问题

目录 1. 第一题 2. 第二题 3. inline 函数可以是虚函数吗 4. 静态成员函数可以是虚函数吗 5. 构造函数可以是虚函数吗 6. 析构函数可以是虚函数吗 7. 拷贝构造和赋值运算符重载可以是虚函数吗 8. 对象访问普通函数快还是访问虚函数快 9. 虚函数表是什么阶段生成的&…

华为与达梦数据签署全面合作协议

4月26日&#xff0c;武汉达梦数据库股份有限公司&#xff08;简称“达梦数据”&#xff09;与华为技术有限公司&#xff08;简称“华为”&#xff09;在达梦数据武汉总部签署全面合作协议。 达梦数据总经理皮宇、华为湖北政企业务总经理吕晓龙出席并见证签约&#xff1b;华为湖…

uniapp、web网页跨站数据交互及通讯

来来来&#xff0c;说说你的创作灵感&#xff01;这就跟吃饭睡觉一样&#xff0c;饿了就找吃的&#xff0c;渴了就倒水张口灌。 最近一个多月实在是忙的没再更新日志&#xff0c;好多粉丝私信说之前的创作于他们而言非常有用&#xff01;受益菲浅&#xff0c;这里非常感谢粉丝…

Ubuntu20.04 设置路由器

1. 网络拓扑图 2. 查看网卡信息 ip a得出如下网卡信息&#xff0c;enp1s0和enp2s0为两个网卡名称&#xff0c;以及相关两个网卡的详细信息&#xff0c;不同设备的网卡名称可能不一样 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group defaul…

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-15.4讲 GPIO中断实验-IRQ中断服务函数详解

前言&#xff1a; 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸机篇”视频的学习笔记&#xff0c;在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…

debug技巧之本地调试

大家好啊&#xff0c;我是summo&#xff0c;今天给大家分享一下我平时是怎么调试代码的&#xff0c;不是权威也不是教学&#xff0c;就是简单分享一下&#xff0c;如果大家还有更好的调试方式也可以多多交流哦。 如果看过我文章的同学应该知道我是一个Java开发&#xff0c;平时…

FANUC机器人工具坐标偏移的用法

一、工具坐标偏移的使用场景 在机器人位置不改变的情况下&#xff0c;工业机器人使用默认工具坐标系示教的一系列运动点位&#xff0c;要保持原本点位位置不变的情况下&#xff0c;改变机器人工具坐标的参数&#xff0c;就要用到机器人坐标转化的功能。在FANUC机器人上体现为机…

/swagger/index.html#/ 的页面可能存在问题,或者已永久移动到新的网址。

问题背景 在golang的项目中&#xff0c;使用了swagger。在另外一个项目中也使用了swagger,没有发生过这个问题。新的项目中&#xff0c;用了和之前项目同样的web框架&#xff0c;仔细比对了和之前项目的差异&#xff0c;只不过&#xff0c;目录结构做了调整&#xff0c;所以&a…

网络编程套接字 (二)---udosocket

本专栏内容为&#xff1a;Linux学习专栏&#xff0c;分为系统和网络两部分。 通过本专栏的深入学习&#xff0c;你可以了解并掌握Linux。 &#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;网络 &#x1f69a;代码仓库&#xff1a;小小unicorn的代…