通讯录的实现(单链表版本)

我们首先要知道通讯录的实现是基于单链表的基础上的,所以我们首先要搞懂单链表。(注意:今天的代码量较多),但这不是阻挡我们前进的脚步,冲冲冲!!!

单链表的简要概述

我们知道单链表是和顺序表有一点区别的:顺序表在物理存储结构上是连续的,在逻辑上也是连续的,而单链表只是在逻辑上是连续的,物理上并不连续。单链表是由一个一个的节点(每个节点由一个结构体组成)连接而成的,它们的地址并不连续,只是它每个节点里面存着下一个节点的地址,我们可以由某一个节点来找到它下一个节点,相当于每个节点依次这样链接起来,这就叫做逻辑上是连续的,而物理上不连续。像这样:

那么我们来实现一下这样的单链表。

简单定义和说明

我们将实现链表的增删查改写在SList.c的源文件里面,将各种函数声明写在SList.h中,将各种函数的测试写在test_1.c的源文件里面。我们来看看我们的头文件里面要实现的函数以及要使用到的头文件。

#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);

注意:这里节点结构体重命名的原因是节点在后续中要多次用到,简化一下代码,而int重命名的原因是我们可以在此插入字符型数据,结构体数据类型,不止于int类型数据。

函数的实现

尾插函数的实现

我们要知道的是刚开始的时候链表是空的,我们要在一个空链表里面一个一个插入数据,而第一个插入的节点则被当作第一个节点,那么这个链表的起始位置要被改变。所以在形参中我们要传二级指针(要改变实参的地址)。而我们要知道的是再插入节点之前,我们要先申请节点空间才能插入进去。

//申请节点
SLTNode* SLTBuyNode(SLTDataType x)
{SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));//申请节点if (newnode == NULL){perror("malloc fail!");return NULL;}newnode->data = x;newnode->next = NULL;return newnode;
}//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{assert(pphead);SLTNode* newnode = SLTBuyNode(x);//尾插前先申请空间然后才能插入进去//分为空链表和非空链表if (*pphead == NULL){*pphead = newnode;}else{//找尾节点SLTNode* ptail = *pphead;while (ptail->next)//如果这里是空链表的话,会报错,不能对空指针进行解引用{ptail = ptail->next;}//找到了尾节点ptail->next = newnode;}
}

头插函数的实现

和尾插一样,插入之前要先申请一块节点空间再去插入。

//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{assert(pphead);SLTNode* newnode = SLTBuyNode(x);newnode->next = *pphead;//不管是空链表还是非空链表都可以插入,不像尾插要分情况*pphead = newnode;
}

尾删函数的实现

只有一个节点的链表和有多个节点的链表的情况有点不同,所以我们要分两种情况去讨论一下,当然在删除之前,链表不能为空。

//尾删
void SLTPopBack(SLTNode** pphead)
{//链表不能为空assert(pphead && *pphead);//判断链表节点有多个还是一个,如果没有分两种情况,程序出问题(结果出问题,但没报错)if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else{SLTNode* prev = *pphead;SLTNode* ptail = *pphead;while (ptail->next){prev = ptail;ptail = ptail->next;}free(ptail);ptail = NULL;prev->next = NULL;}
}

头删函数的实现

和尾删一样,在删除之前链表不能为空。但不需要分情况。

//头删
void SLTPopFront(SLTNode** pphead)
{//前提是链表不为空assert(pphead && *pphead);//不管链表是只有一个还是多个,都行通SLTNode* next = (*pphead)->next;free(*pphead);*pphead = next;
}

查找函数的实现

我们需要遍历链表,查找数据,查到了就返回节点位置,没有查到返回空。

//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{SLTNode* pur = phead;while (pur){if (pur->data == x){return pur;}pur = pur->next;}//遍历了一遍,没有找到return NULL;
}

在指定位置之前插入节点函数实现

要实现在指定的节点位置插入节点,我们需要利用上一个查找节点的函数来找到指定位置,然后进行下一步的实现。

//在指定位置之前插入
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{//不能为空链表assert(pphead && *pphead);assert(pos);SLTNode* newnode = SLTBuyNode(x);//如果在头节点之前插入,将找不到pos,所以分两种情况if (*pphead == pos){SLTPushFront(pphead, x);}//利用头插函数就行else{SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}//将 prev newnode pos 链接起来prev->next = newnode;newnode->next = pos;}
}

在指定位置之后插入节点函数的实现

//在指定位置之后插入
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)
{//链表不能为空以及pos不能为空assert(pphead && *pphead);assert(pos);//两种情况一种pos为头节点,一种不为头节点if (*pphead == pos){//头删SLTPopFront(pphead);}else{SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}//将prev pos pos->next 中的pos删除prev->next = pos->next;free(pos);pos = NULL;}
}

删除指定位置之后的位置节点函数的实现

我们要区分要下删除指定位置的节点函数的代码逻辑。

//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos)
{//pos之后不能为空,要不然怎么删除assert(pos && pos->next);SLTNode* del = pos->next;//pos del del->next  删除pospos->next = del->next;free(del);del = NULL;
}

链表的销毁

链表节点创建出来,不用了的话我们就要销毁,养成好的代码习惯。

//链表的销毁
void SListDestroy(SLTNode** pphead)
{//空链表就不需要销毁assert(pphead && *pphead);SLTNode* pur = *pphead;while (pur){SLTNode* next = pur->next;free(pur);pur = next;}*pphead = NULL;
}

通讯录的简单定义和说明

我们将通讯录的代码实现要用到的函数实现写到Contact.c的源文件中,将所用到的函数声明写到Contact.h文件中。

Contact.h函数声明如下:

#pragma once
#define NAME_MAX 100
#define SEX_MAX 4
#define TEL_MAX 11
#define ADDR_MAX 100
//前置声明
typedef struct SListNode contact;
//用户数据
typedef struct PersonInfo
{char name[NAME_MAX];//姓名char sex[SEX_MAX];//性别int age;//年龄char tel[TEL_MAX];//电话char addr[ADDR_MAX];//地址
}PeoInfo;
//初始化通讯录
void InitContact(contact** con);
//添加通讯录数据
void AddContact(contact** con);
//删除通讯录数据
void DelContact(contact** con);
//展示通讯录数据
void ShowContact(contact* con);
//查找通讯录数据
void FindContact(contact* con);
//修改通讯录数据
void ModifyContact(contact** con);
//销毁通讯录数据
void DestroyContact(contact** con);

初始化通讯录函数的实现

//初始化通讯录
void InitContact(contact** con)
{*con = NULL;
}

添加通讯录数据函数的实现

注意:这个函数的实现,可以调用单链表函数来实现。调用尾插或头插都行。

//添加通讯录数据
void AddContact(contact** con)
{//尾插和头插两种方法PeoInfo infor;printf("请输入姓名!\n");scanf("%s", infor.name);printf("请输入性别!\n");scanf("%s", infor.sex);printf("请输入年龄!\n");scanf("%d", &(infor.age));printf("请输入电话!\n");scanf("%s", infor.tel);printf("请输入地址!\n");scanf("%s", infor.addr);//采用尾插插入数据,也可采用头插,单链表中有现成的函数SLTPushBack(con, infor);
}

展示通讯录数据函数的实现

//展示通讯录数据
void ShowContact(contact* con)
{printf("姓名  性别  年龄  电话  地址\n");contact* pcur = con;while (pcur){printf("%s     %s     %d    %s   %s\n", pcur->data.name,pcur->data.sex,pcur->data.age,pcur->data.tel,pcur->data.addr);pcur = pcur->next;}
}

查找通讯录的数据函数的实现

//查找通讯录数据
void FindContact(contact* con)
{char name[NAME_MAX];printf("请输入要查找联系人的信息!\n");scanf("%s", name);contact* pcur = con;while (pcur){if (strcmp(pcur->data.name, name) == 0){printf("姓名  性别  年龄  电话  地址\n");printf("%s     %s     %d    %s   %s\n", pcur->data.name, pcur->data.sex,pcur->data.age, pcur->data.tel,pcur->data.addr);return;}pcur = pcur->next;}printf("没有此联系人!\n");
}

删除通讯录函数的实现

我们首先要找到你需要删除的联系人地址,所以我们要封装另一个函数。并且删除函数单链表里面有现成函数,我们可以直接调用。

//查找联系人数据的指针
contact* Findaddr(contact** con,char*name)
{contact* pcur = *con;while (pcur){if (strcmp(pcur->data.name, name) == 0){return pcur;}pcur = pcur->next;}return NULL;
}
//删除通讯录数据
void DelContact(contact** con)
{assert(con && &con);char name[NAME_MAX];printf("请输入你要删除的联系人姓名!\n");scanf("%s", name);contact*find=Findaddr(con, name);if (find == NULL){printf("没有此联系人!\n");}else{SLTErase(con, find);printf("删除成功!\n");}
}

修改通讯录数据函数的实现

//修改通讯录数据
void ModifyContact(contact** con)
{//修改之前联系人要存在char name[NAME_MAX];printf("请输入要修改的联系人姓名!\n");scanf("%s", name);contact* find = Findaddr(con, name);if (find == NULL){printf("该联系人不存在!\n");return;}else{PeoInfo infor;printf("请输入新的姓名!\n");scanf("%s", infor.name);printf("请输入新的性别!\n");scanf("%s", infor.sex);printf("请输入新的年龄!\n");scanf("%d", &(infor.age));printf("请输入新的电话!\n");scanf("%s", infor.tel);printf("请输入新的地址!\n");scanf("%s", infor.addr);find->data = infor;}
}

销毁通讯录数据函数的实现

//销毁通讯录数据
void DestroyContact(contact** con)
{assert(con && *con);SListDestroy(con);
}

通讯录菜单的封装

最终我们将所以函数进行一个封装汇总。

void menu()
{printf("********通讯录*******\n");printf("请选择你的操作:\n");printf("1.添加联系人   2.删除联系人\n");printf("3.展示联系人   4.查找联系人\n");printf("5.修改联系人   0.退出通讯录\n");
}
int main()
{contact* con;InitContact(&con);//初始化int input;do{menu();scanf("%d", &input);switch (input){case 1:AddContact(&con);break;case 2:DelContact(&con);break;case 3:ShowContact(con);break;case 4:FindContact(con);break;case 5:ModifyContact(&con);break;case 0:printf("退出成功!\n");break;default:printf("选项错误,请重新选择!\n");break;}} while (input);DestroyContact(&con);return 0;
}

总代码汇总

SList.c文件

#include"SList.h"
//打印
void SLTprint(SLTNode* phead)
{SLTNode* pur = phead;while (pur){printf("%d->", pur->data);pur = pur->next;}printf("NULL\n");
}
//申请节点
SLTNode* SLTBuyNode(SLTDataType x)
{SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));//申请节点if (newnode == NULL){perror("malloc fail!");return NULL;}newnode->data = x;newnode->next = NULL;return newnode;
}//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{assert(pphead);SLTNode* newnode = SLTBuyNode(x);//尾插前先申请空间然后才能插入进去//分为空链表和非空链表if (*pphead == NULL){*pphead = newnode;}else{//找尾节点SLTNode* ptail = *pphead;while (ptail->next)//如果这里是空链表的话,会报错,不能对空指针进行解引用{ptail = ptail->next;}//找到了尾节点ptail->next = newnode;}
}
//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{assert(pphead);SLTNode* newnode = SLTBuyNode(x);newnode->next = *pphead;//不管是空链表还是非空链表都可以插入,不像尾插要分情况*pphead = newnode;
}
//尾删
void SLTPopBack(SLTNode** pphead)
{//链表不能为空assert(pphead && *pphead);//判断链表节点有多个还是一个,如果没有分两种情况,程序出问题(结果出问题,但没报错)if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else{SLTNode* prev = *pphead;SLTNode* ptail = *pphead;while (ptail->next){prev = ptail;ptail = ptail->next;}free(ptail);ptail = NULL;prev->next = NULL;}
}
//头删
void SLTPopFront(SLTNode** pphead)
{//前提是链表不为空assert(pphead && *pphead);//不管链表是只有一个还是多个,都行通SLTNode* next = (*pphead)->next;free(*pphead);*pphead = next;
}
//查找
//SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
//{
//	SLTNode* pur = phead;
//	while (pur)
//	{
//		if (pur->data == x)
//		{
//			return pur;
//		}
//		pur = pur->next;
//	}
//	遍历了一遍,没有找到
//	return NULL;
//}
//在指定位置之前插入
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{//不能为空链表assert(pphead && *pphead);assert(pos);SLTNode* newnode = SLTBuyNode(x);//如果在头节点之前插入,将找不到pos,所以分两种情况if (*pphead == pos){SLTPushFront(pphead, x);}//利用头插函数就行else{SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}//将 prev newnode pos 链接起来prev->next = newnode;newnode->next = pos;}
}
//在指定位置之后插入
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)
{//链表不能为空以及pos不能为空assert(pphead && *pphead);assert(pos);//两种情况一种pos为头节点,一种不为头节点if (*pphead == pos){//头删SLTPopFront(pphead);}else{SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}//将prev pos pos->next 中的pos删除prev->next = pos->next;free(pos);pos = NULL;}
}
//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos)
{//pos之后不能为空,要不然怎么删除assert(pos && pos->next);SLTNode* del = pos->next;//pos del del->next  删除pospos->next = del->next;free(del);del = NULL;
}
//链表的销毁
void SListDestroy(SLTNode** pphead)
{//空链表就不需要销毁assert(pphead && *pphead);SLTNode* pur = *pphead;while (pur){SLTNode* next = pur->next;free(pur);pur = next;}*pphead = NULL;
}

SList.h文件

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include"Contact.h"
//定义节点
typedef PeoInfo 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);

Contact.h文件

#pragma once
#define NAME_MAX 100
#define SEX_MAX 4
#define TEL_MAX 11
#define ADDR_MAX 100
//前置声明
typedef struct SListNode contact;
//用户数据
typedef struct PersonInfo
{char name[NAME_MAX];//姓名char sex[SEX_MAX];//性别int age;//年龄char tel[TEL_MAX];//电话char addr[ADDR_MAX];//地址
}PeoInfo;
//初始化通讯录
void InitContact(contact** con);
//添加通讯录数据
void AddContact(contact** con);
//删除通讯录数据
void DelContact(contact** con);
//展示通讯录数据
void ShowContact(contact* con);
//查找通讯录数据
void FindContact(contact* con);
//修改通讯录数据
void ModifyContact(contact** con);
//销毁通讯录数据
void DestroyContact(contact** con);

Contact.c文件

#include"SList.h"
//初始化通讯录
void InitContact(contact** con)
{*con = NULL;
}
//添加通讯录数据
void AddContact(contact** con)
{//尾插和头插两种方法PeoInfo infor;printf("请输入姓名!\n");scanf("%s", infor.name);printf("请输入性别!\n");scanf("%s", infor.sex);printf("请输入年龄!\n");scanf("%d", &(infor.age));printf("请输入电话!\n");scanf("%s", infor.tel);printf("请输入地址!\n");scanf("%s", infor.addr);//采用尾插插入数据,也可采用头插,单链表中有现成的函数SLTPushBack(con, infor);
}
//展示通讯录数据
void ShowContact(contact* con)
{printf("姓名  性别  年龄  电话  地址\n");contact* pcur = con;while (pcur){printf("%s     %s     %d    %s   %s\n", pcur->data.name,pcur->data.sex,pcur->data.age,pcur->data.tel,pcur->data.addr);pcur = pcur->next;}
}
//查找通讯录数据
void FindContact(contact* con)
{char name[NAME_MAX];printf("请输入要查找联系人的信息!\n");scanf("%s", name);contact* pcur = con;while (pcur){if (strcmp(pcur->data.name, name) == 0){printf("姓名  性别  年龄  电话  地址\n");printf("%s     %s     %d    %s   %s\n", pcur->data.name, pcur->data.sex,pcur->data.age, pcur->data.tel,pcur->data.addr);return;}pcur = pcur->next;}printf("没有此联系人!\n");
}
//查找联系人数据的指针
contact* Findaddr(contact** con,char*name)
{contact* pcur = *con;while (pcur){if (strcmp(pcur->data.name, name) == 0){return pcur;}pcur = pcur->next;}return NULL;
}
//删除通讯录数据
void DelContact(contact** con)
{assert(con && &con);char name[NAME_MAX];printf("请输入你要删除的联系人姓名!\n");scanf("%s", name);contact*find=Findaddr(con, name);if (find == NULL){printf("没有此联系人!\n");}else{SLTErase(con, find);printf("删除成功!\n");}
}
//修改通讯录数据
void ModifyContact(contact** con)
{//修改之前联系人要存在char name[NAME_MAX];printf("请输入要修改的联系人姓名!\n");scanf("%s", name);contact* find = Findaddr(con, name);if (find == NULL){printf("该联系人不存在!\n");return;}else{PeoInfo infor;printf("请输入新的姓名!\n");scanf("%s", infor.name);printf("请输入新的性别!\n");scanf("%s", infor.sex);printf("请输入新的年龄!\n");scanf("%d", &(infor.age));printf("请输入新的电话!\n");scanf("%s", infor.tel);printf("请输入新的地址!\n");scanf("%s", infor.addr);find->data = infor;}
}
//销毁通讯录数据
void DestroyContact(contact** con)
{assert(con && *con);SListDestroy(con);
}

test_1.c文件

#include"SList.h"
void menu()
{printf("********通讯录*******\n");printf("请选择你的操作:\n");printf("1.添加联系人   2.删除联系人\n");printf("3.展示联系人   4.查找联系人\n");printf("5.修改联系人   0.退出通讯录\n");
}
int main()
{contact* con;InitContact(&con);//初始化int input;do{menu();scanf("%d", &input);switch (input){case 1:AddContact(&con);break;case 2:DelContact(&con);break;case 3:ShowContact(con);break;case 4:FindContact(con);break;case 5:ModifyContact(&con);break;case 0:printf("退出成功!\n");break;default:printf("选项错误,请重新选择!\n");break;}} while (input);DestroyContact(&con);return 0;
}

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

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

相关文章

2024.4.19 Python爬虫复习day07 可视化3

综合案例 需求: 已知2020年疫情数据,都是json数据,需要从文件中读出,进行处理和分析,最终实现数据可视化折线图 相关知识点: json json简介: 本质是一个特定格式的字符串 举例: [{},{},{}] 或者 {}python中json包: import jsonpython数据转为json数据: 变量接收json…

微服务架构使用和docker部署方法(若依)

这里以若依官方网站开源的微服务框架为例子记录使用方法过程。 开源地址&#xff1a;RuoYi-Cloud: &#x1f389; 基于Spring Boot、Spring Cloud & Alibaba的分布式微服务架构权限管理系统&#xff0c;同时提供了 Vue3 的版本 下载后&#xff0c;用IDEA社区版开发工具打…

GNU Radio Radar Toolbox编译及安装

文章目录 前言一、GNU Radio Radar Toolbox 介绍二、gr-radar 安装三、具体使用四、OFDM 雷达仿真 前言 GNU Radio Radar Toolbox&#xff08;gr-radar&#xff09;是一个开放源码的工具箱&#xff0c;用于 GNU Radio 生态系统&#xff0c;主要目的是为雷达信号处理提供必要的…

vue源码解析——diff算法/双端比对/patchFlag/最长递增子序列

虚拟dom——virtual dom&#xff0c;提供一种简单js对象去代替复杂的 dom 对象&#xff0c;从而优化 dom 操作。virtual dom 是“解决过多的操作 dom 影响性能”的一种解决方案。virtual dom 很多时候都不是最优的操作&#xff0c;但它具有普适性&#xff0c;在效率、可维护性之…

木马免杀代码之python反序列化分离免杀

本篇文章主要用到python来对CobaltStrike生成的Shellcode进行分离免杀处理, 因此要求读者要有一定的python基础, 下面我会介绍pyhon反序列化免杀所需用到的相关函数和库 exec函数 exec函数是python的内置函数, 其功能与eval()函数相同, 但不同的是exec函数支持多行python代码…

我国新戊二醇产能逐渐增长 市场集中度有望进一步提升

我国新戊二醇产能逐渐增长 市场集中度有望进一步提升 新戊二醇&#xff08;NPG&#xff09;又称为2,2-二甲基-1,3-丙二醇&#xff0c;化学式为C5H12O2&#xff0c;熔点为124-130℃。新戊二醇多表现为一种无特殊气味的白色结晶固体&#xff0c;易溶于水及醇、醚等溶液。新戊二醇…

为什么看到这么多人不推荐C++?

前几天逛知乎的时候&#xff0c;看到一个问题&#xff1a; 看到这个问题我倒是想吐槽几句了。 C一直没找到自己的定位&#xff01; C语言&#xff1a;我是搞系统编程开发的&#xff0c;操作系统、数据库、编译器、网络协议栈全是我写的。 PHP&#xff1a;我是搞后端业务开发…

一年期SSL证书怎么申请?

申请SSL证书三步走 JoySSL_JoySSL SSL证书_JoySSL https证书-JoySSL 一、选证书类型 根据网站性质与安全需求&#xff0c;选定合适的SSL证书&#xff1a; - 域名验证证书&#xff08;DV&#xff09;&#xff1a;快速验证域名所有权&#xff0c;适用于个人网站、博客&#xff…

ReentrantLock源码阅读

1. 概述 lock锁, 基于队列同步器AQS, 实现公平锁、非公平锁 队列同步器AQS可以阅读我这篇文章&#xff1a; 点击传送 实现了Lock接口: public class ReentrantLock implements Lock// 加锁 获取不到锁一直等待 void lock(); // 加锁 获取不到锁一直等待 等待过程可以被中断…

【攻防世界】php_rce (ThinkPHP5)

进入题目环境&#xff0c;查看页面信息&#xff1a; 页面提示 ThinkPHP V5&#xff0c;猜测存在ThinkPHP5 版本框架的漏洞&#xff0c;于是查找 ThinkPHP5 的攻击POC。 构造 payload: http://61.147.171.105:50126/?sindex/think\app/invokefunction&functioncall_user_f…

【Go语言快速上手(一)】 初识Go语言

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:Go语言专栏⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习更多Go语言知识   &#x1f51d;&#x1f51d; Go快速上手 1. 前言2. Go语言简介(为…

模拟Android系统Zygote启动流程

版权声明&#xff1a;本文为梦想全栈程序猿原创文章&#xff0c;转载请附上原文出处链接和本声明 前言&#xff1a; 转眼时间过去了10年了&#xff0c;回顾整个10年的工作历程&#xff0c;做了3年的手机&#xff0c;4年左右的Android指纹相关的工作&#xff0c;3年左右的跟传感…

亚马逊CloudFront使用体验

前言 首先在体验CloudFront之前&#xff0c;先介绍一下什么是CDN&#xff0c;以及CDN的基本原理。 CDN是Content Delivery Network&#xff08;内容分发网络&#xff09;的缩写&#xff0c;是一种利用分布式节点技术&#xff0c;在全球部署服务器&#xff0c;即时地将网站、应…

《由浅入深学习SAP财务》:第2章 总账模块 - 2.6 定期处理 - 2.6.6 年初操作:科目余额结转

2.6.6 年初操作&#xff1a;科目余额结转 在使用事务代码 FAGLB03 查询科目余额时&#xff0c;可以看到按期间的发生额清单。其中&#xff0c;第一行称为“余额结转”&#xff0c;该行的累计余额代表上年度遗留下来的余额&#xff0c;也就是年初余额。对于资产负债表科目而言&a…

七大设计原则

在软件开发的领域中&#xff0c;随着技术的不断进步和市场需求的不断变化&#xff0c;软件系统的设计和维护变得越来越重要。为了确保软件系统能够长期有效地运行&#xff0c;并且能够在未来的发展中适应新的需求和技术变化&#xff0c;提高软件系统的可维护性和可复用性成为了…

map与set

set使用 set在我们就是我们前面学习的k模型&#xff0c;它可以用来比对数据&#xff0c;增删查的时间复杂度都是O&#xff08;logn&#xff09;效率非常高&#xff0c;由于它底层的原因&#xff0c;它也可以实现排序&#xff0c;通过中序遍历可以输出我们的有序的数据&#xff…

安装gensim报错

安装gensim pip install --upgrade gensim装完以后注意一个 装了一堆库其实&#xff0c;看下对应的scipy版本是1.13.0 然后运行 import gensim报错&#xff1a; cannot import name ‘triu’ from ‘scipy.linalg’ https://www.soinside.com/question/brZ46N5EH7bk9xdVwXa…

华为OD机试 - 内存冷热标记(Java 2024 C卷 100分)

华为OD机试 2024C卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷C卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;每一题都有详细的答题思路、详细的代码注释、样例测试…

Springboot+Vue项目-基于Java+MySQL的高校心理教育辅导系统(附源码+演示视频+LW)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &…

【Linux网络编程】TCP协议

TCP协议 1.TCP协议段格式4位首位长度序号和确认序号16位窗口大小6个标志位 2.确认应答机制3.超时重传机制4.连接管理机制如何理解连接如何理解三次握手如何理解四次挥手 5.流量控制6.滑动窗口7.拥塞控制8.延迟应答9.捎带应答10.面向字节流11.粘包问题12.TCP异常情况13.TCP小结1…