【C语言】动态通讯录 -- 详解

news2024/9/25 17:20:56

⚪前言

前面详细介绍了静态版通讯录【C语言】静态通讯录 -- 详解_炫酷的伊莉娜的博客-CSDN博客,但是静态版通讯录的空间是无法被改变的,而且空间利用率也不高。为了解决静态通讯录这一缺点,这时就要有一个能够随着存入联系人数量的增加而增大空间的通讯录。接下来我们将探讨动态通讯录,在能够实现静态版通讯录的基础上,能够灵活调整通讯录联系人数量并实现其基本功能。

以下内容是在静态通讯录的基础上进行修改,大致思路不变。


一、实现目标

1、功能

  • 保存 1000 个联系人的信息
  • 添加联系人
  • 删除联系人
  • 修改联系人
  • 查找联系人
  • 排序

2、个人信息(结构体)

  • 名字
  • 年龄
  • 性别
  • 电话
  • 地址

动态版通讯录的要求:

  1. 默认能够存放 3 个联系人信息。
  2. 不够的话,每次增加 2 个联系人信息。

二、创建文件

  1. test.c(专门测试通讯录的功能)
  2. contact.c(接口的实现)
  3. contact.h(接口的声明)

三、初始页面的实现

1、实现主菜单页面

// test.c

(1)menu()
void menu()
{
	printf("****************************************\n");
	printf("****** 1.add           2.del    ********\n");
	printf("****** 3.search        4.modify ********\n");
	printf("****** 5.show          6.sort   ********\n");
	printf("****** 0.exit                   ********\n");
	printf("****************************************\n");
}

效果图如下:


(2)main()
int main()
{
	int input = 0;
	// 创建通讯录
	struct Contact con; // con就是通讯录,里边包含:data指针和size,capacity
	// 初始化通讯录
	InitContact(&con);

	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			AddContact(&con);
			break;
		case DEL:
			DelContact(&con);
			break;
		case SEARCH:
			SearchContact(&con);
			break;
		case MODIFY:
			ModifyContact(&con);
			break;
		case SHOW:
			ShowContact(&con);
			break;
		case SORT:
			SortContact(&con);
			break;
		case EXIT:
			DestoryContact(&con); // 销毁通讯录-释放动态开辟的内存
			printf("退出通讯录!\n");
			break;
		default:
			printf("选择错误!\n");
			break;
		}
	} while (input);
	return 0;
}

main() 函数代码中与之前的区别主要就是要在初始化的时候开辟动态内存空间,在输入 0 退出程序时,会将通讯录删除。其实仔细想想,确实需要这一步,因为在整个程序都没有结束时,动态开辟内存也并没有结束,所以只能是在整个程序结束的时候才能将动态开辟的内存释放掉。 


2、在contact.h 中引用所需要的文件和必要的定义

首先我们要 声明一个结构体,其中 包含了我们想要保存的信息(可以自己设置):
typedef struct PeoInfo
{
	char name[MAX_NAME];
	int age;
	char sex[MAX_SEX];
	char tele[MAX_TELE];
	char addr[MAX_ADDR];
}PeoInfo;

 定义结构体(与静态通讯录不同!)

        动态通讯录在定义结构体时,应该增加一个元素(capacity)来记录当前通讯录的最大容量,当达到这个最大容量的时候,就对动态通讯录进行扩容,增加内存空间,这样就能够很好地实现动态通讯录。 

        静态通讯录与动态通讯录最大的不同就在这,静态版是直接使用一个开辟好的数组,而动态版是使用一个指针变量,用于指向使用动态内存申请函数 malloc 所开辟的空间,然后使用开辟的这块空间对联系人信息进行储存,最大的优点是可以使用 realloc 函数对其进行扩容。这样就解决了静态通讯录在空间上的不足。

// 动态通讯录类型
typedef struct Contact
{
	struct PeoInfo *data; // 存放数据
	int size; // 记录当前已经有的元素个数
	int capacity; // 记录当前通讯录的最大容量
}Contact;

// 静态通讯录类型
struct Contact
{
	struct PeoInfo data[MAX];// 可以存放1000个人的信息
	int size;// 记录通讯录中已经保存的信息个数
};

整体逻辑:

// contact.h
#pragma once // 避免头文件被重复引用

// #define MAX 1000

#define DEFAULT_SZ 3 // 通讯录初始状态的容量大小

#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TELE 12
#define MAX_ADDR 30

#include <stdio.h>
#include <string.h> // memset strcmp
#include <stdlib.h> // qsort malloc realloc
#include <assert.h> // assert

enum Option
{
	EXIT,//0
	ADD,//1
	DEL,//2
	SEARCH,//3
	MODIFY,//4
	SHOW,//5
	SORT//6
};

struct PeoInfo
{
	char name[MAX_NAME];
	int age;
	char sex[MAX_SEX];
	char tele[MAX_TELE];
	char addr[MAX_ADDR];
};

// 动态通讯录类型
typedef struct Contact
{
	struct PeoInfo *data; // 存放数据
	int size; // 记录当前已经有的元素个数
	int capacity; // 记录当前通讯录的最大容量
}Contact;

// 声明函数
// 初始化通讯录
void InitContact(struct Contact* ps);
// 增加联系人
void AddContact(struct Contact* ps);
// 展示通讯录信息
void ShowContact(const struct Contact* ps);
// 删除指定联系人信息
void DelContact(struct Contact* ps);
// 查找指定联系人信息
void SearchContact(const struct Contact* ps);
// 修改指定联系人信息
void ModifyContact(struct Contact* ps);
// 排序通讯录内容(按姓名排)
void SortContact(struct Contact* ps);
// 销毁通讯录
void DestoryContact(Contact* ps);

四、在 contact.c 上实现各个接口函数

1、初始化通讯录

malloc 函数在这里并没有把其数据初始化为 0 ,这里可以使用 memset 函数初始化存放联系人信息的结构体数组 data。

对于动态通讯录的初始化,就是申请一块初始的空间,用来存放信息,如果满了在在此基础上进行扩容。

// 初始化通讯录
void InitContact(struct Contact* ps)
{
	assert(ps);
	ps->size = 0;
	ps->capacity = DEFAULT_SZ;
	ps->data = (struct PeoInfo*)malloc(ps->capacity * sizeof(struct PeoInfo));
	if (ps->data == NULL)
	{
		perror("InitContact::malloc");
		return;
	}
	memset(ps->data, 0, ps->capacity * sizeof(struct PeoInfo));
}

2、添加联系人

先判断通讯录人数是否已满,满了需要扩容,对已经申请过的空间进行扩容,可以使用库函数 realloc 实现。满了一次扩容 2 联系人信息的空间。

// 检测当前通讯录的容量
void CheckCapacity(struct Contact* ps)
{
	if (ps->size == ps->capacity)
	{
		//增容
		struct PeoInfo* ptr = realloc(ps->data, (ps->capacity + 2) * sizeof(PeoInfo));
		if (ptr != NULL)
		{
			ps->data = ptr;
			ps->capacity += 2;
			printf("增容成功!\n");
		}
		else
		{
			printf("增容失败!\n");
		}
	}
}

// 添加联系人
void AddContact(struct Contact* ps)
{
	assert(ps);

	// 检测当前通讯录的容量
	// 1、如果满了就增加空间
	// 2、如果没满无需任何操作
	CheckCapacity(ps);
	// 增加数据
	printf("请输入名字:>");
	scanf("%s", ps->data[ps->size].name);
	printf("请输入年龄:>");
	scanf("%d", &(ps->data[ps->size].age)); // 年龄是一个整型变量 需要取地址
	printf("请输入性别:>");
	scanf("%s", ps->data[ps->size].sex);
	printf("请输入电话:>");
	scanf("%s", ps->data[ps->size].tele);
	printf("请输入地址:>");
	scanf("%s", ps->data[ps->size].addr);

	ps->size++;
	printf("添加成功!\n");
}

 注意由于 age 在这里是一个整型变量,所以要加上 &


3、展示通讯录中的信息

// 展示通讯录中的信息
void ShowContact(const struct Contact* ps)
{
	if (ps->size == 0)
	{
		printf("通讯录为空!\n"); // 判断通讯录人数是否为0

	}
	else
	{
		int i = 0;
		// 打印标题
		printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址");
		// 打印数据
		for (i = 0; i < ps->size; i++)
		{
			printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n",
				ps->data[i].name,
				ps->data[i].age,
				ps->data[i].sex,
				ps->data[i].tele,
				ps->data[i].addr);
		}
	}
}

注意:因为只是打印信息,不会修改内容,所以加上 const 更加安全。 

为了让结果更美观,采用了左对齐的方式。printf 中 %-20s 中的 20 是指输出字段的宽度负号表示左对齐,如省略表示右对齐,如果输出的数据位数小于 20,则在数据右端补齐空格。


4、通过姓名查找指定联系人所在的下标 

// 通过姓名查找指定联系人所在下标
static int FindByName(const struct Contact* ps, char name[MAX_NAME])
{
	int i = 0;
	for (i = 0; i < ps->size; i++)
	{
		if (0 == strcmp(ps->data[i].name, name))
		{
			return i; // 查找到返回联系人的下标
		}
	}

	return -1; // 找不到返回-1
}

为了解决代码的冗余,我们不妨分装一个函数来完成查找功能。该函数会在删除 / 修改 / 查找指定联系人信息的函数中被调用到,这些函数刚好又在同一源文件中,所以用 static 修饰函数,只能在所在此文件内使用。


5、删除指定联系人信息

先判断通讯录是否为空,如果不为空,再通过 FindByName() 函数查找通讯录中是否有你要删除的联系人,如果有则删除。

原理:把要删除的联系人后面一个人的信息依次从前向后往前移动一位,覆盖掉其信息,然后再将人数 -1。

// 删除指定联系人
void DelContact(struct Contact* ps)
{
	char name[MAX_NAME];
	int pos = 0;
	printf("请输入要删除人的名字:>");
	scanf("%s", name);
	// 1、查找要删除的人在什么位置
	// 找到了返回名字所在元素的下标
	// 找不到返回 -1
	pos = FindByName(ps, name);

	// 2、删除
	if (pos == -1)
	{
		printf("要删除的人不存在!\n");
	}
	else
	{
		// 删除数据
		int j = 0;
		for (j = pos; j < ps->size - 1; j++)
		{
            // 把要删除的人后面一个人的信息依次从前向后往前移动一位,覆盖掉其信息
			ps->data[j] = ps->data[j + 1];
		}
		ps->size--;
		printf("删除成功!\n");
	}
}

6、查找指定联系人信息

通过 FindByName() 函数查找通讯录中是否有你要查找的联系人,如果查找到,则打印该联系人的信息。 

// 查找指定联系人信息
void SearchContact(const struct Contact* ps)
{
	int pos = 0;
	char name[MAX_NAME];
	printf("请输入要查找人的名字:>");
	scanf("%s", name);
	pos = FindByName(ps, name); // 查找
	if (pos == -1)
	{
		printf("要查找的人不存在!\n");
	}
	else
	{
		printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址");
		//数据
		printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n",
			ps->data[pos].name,
			ps->data[pos].age,
			ps->data[pos].sex,
			ps->data[pos].tele,
			ps->data[pos].addr);
	}
}

 注意:因为只是查找信息,不会修改内容,所以加上 const 更加安全 。


7、修改指定联系人信息

通过 FindByName() 函数查找通讯录中是否有你要修改信息的联系人,如果有则修改。 

// 修改指定联系人信息
void ModifyContact(struct Contact* ps)
{
	int pos = 0;
	char name[MAX_NAME];
	printf("请输入要修改人的名字:>");
	scanf("%s", name);
	pos = FindByName(ps, name);
	if (pos == -1)
	{
		printf("要修改人的信息不存在!\n");
	}
	else
	{
		printf("请输入名字:>");
		scanf("%s", ps->data[pos].name);
		printf("请输入年龄:>");
		scanf("%d", &ps->data[pos].age);
		printf("请输入性别:>");
		scanf("%s", ps->data[pos].sex);
		printf("请输入电话:>");
		scanf("%s", ps->data[pos].tele);
		printf("请输入地址:>");
		scanf("%s", ps->data[pos].addr);

		printf("修改完成!\n");
	}
}

8、排序通讯录内容

// 排序通讯录内容
// 比较结构体数组中两个元素的姓名成员
int compareStructType_name(const void* elem1, const void* elem2)
{
	// 姓名成员是字符串,用strcmp比较
	return strcmp(((struct PeoInfo*)elem1)->name, ((struct PeoInfo*)elem2)->name);
}

// 以名字排序所有联系人
void SortContact(struct Contact* ps)
{
	// 判断通讯录是否为空
	if (ps->size == 0)
	{
		printf("通讯录为空,无法排序!\n");
		return;
	}
	else
	{
		// 根据姓名成员对结构体数组升序排列
		qsort(ps->data, ps->size, sizeof(ps->data[0]), compareStructType_name);
		printf("以名字排序联系人成功!\n");
	}
}

该函数排序用的是姓名作为标准,也可以自己更换其他的排序方式。


9、销毁通讯录

使用动态内存开辟的空间是需要归还的,当通讯录使用完后是需要归还内存的,也就需要我们销毁通讯录。

// 销毁通讯录
void DestoryContact(Contact* ps)
{
	free(ps->data);
	ps->data = NULL;
	ps->capacity = 0;
	ps->size = 0;
	printf("销毁成功!\n");
}

五、代码整合

1、test.c

// test.c
#define _CRT_SECURE_NO_WARNINGS 1

#include "contact.h"

void menu()
{
	printf("****************************************\n");
	printf("****** 1.add           2.del    ********\n");
	printf("****** 3.search        4.modify ********\n");
	printf("****** 5.show          6.sort   ********\n");
	printf("****** 0.exit                   ********\n");
	printf("****************************************\n");
}

int main()
{
	int input = 0;
	// 创建通讯录
	struct Contact con; // con就是通讯录,里边包含:data指针和size,capacity
	// 初始化通讯录
	InitContact(&con);

	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			AddContact(&con);
			break;
		case DEL:
			DelContact(&con);
			break;
		case SEARCH:
			SearchContact(&con);
			break;
		case MODIFY:
			ModifyContact(&con);
			break;
		case SHOW:
			ShowContact(&con);
			break;
		case SORT:
			SortContact(&con);
			break;
		case EXIT:
			DestoryContact(&con); // 销毁通讯录-释放动态开辟的内存
			printf("退出通讯录!\n");
			break;
		default:
			printf("选择错误!\n");
			break;
		}
	} while (input);
	return 0;
}

2、contact.h

// contact.h
#pragma once // 避免头文件被重复引用

// #define MAX 1000

#define DEFAULT_SZ 3 // 通讯录初始状态的容量大小

#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TELE 12
#define MAX_ADDR 30

#include <stdio.h>
#include <string.h> // memset strcmp
#include <stdlib.h> // qsort malloc realloc
#include <assert.h> // assert

enum Option
{
	EXIT,//0
	ADD,//1
	DEL,//2
	SEARCH,//3
	MODIFY,//4
	SHOW,//5
	SORT//6
};

struct PeoInfo
{
	char name[MAX_NAME];
	int age;
	char sex[MAX_SEX];
	char tele[MAX_TELE];
	char addr[MAX_ADDR];
};

// 动态通讯录类型
typedef struct Contact
{
	struct PeoInfo *data; // 存放数据
	int size; // 记录当前已经有的元素个数
	int capacity; // 记录当前通讯录的最大容量
}Contact;

// 声明函数
// 初始化通讯录
void InitContact(struct Contact* ps);
// 增加联系人
void AddContact(struct Contact* ps);
// 展示通讯录信息
void ShowContact(const struct Contact* ps);
// 删除指定联系人信息
void DelContact(struct Contact* ps);
// 查找指定联系人信息
void SearchContact(const struct Contact* ps);
// 修改指定联系人信息
void ModifyContact(struct Contact* ps);
// 排序通讯录内容(按姓名排)
void SortContact(struct Contact* ps);
// 销毁通讯录
void DestoryContact(Contact* ps);

3、contact.c

// contact.c
#define _CRT_SECURE_NO_WARNINGS 1

#include "contact.h"

// 初始化通讯录
void InitContact(struct Contact* ps)
{
	assert(ps);
	ps->size = 0;
	ps->capacity = DEFAULT_SZ;
	ps->data = (struct PeoInfo*)malloc(ps->capacity * sizeof(struct PeoInfo));
	if (ps->data == NULL)
	{
		perror("InitContact::malloc");
		return;
	}
	memset(ps->data, 0, ps->capacity * sizeof(struct PeoInfo));
}

// 检测当前通讯录的容量
void CheckCapacity(struct Contact* ps)
{
	if (ps->size == ps->capacity)
	{
		//增容
		struct PeoInfo* ptr = realloc(ps->data, (ps->capacity + 2) * sizeof(PeoInfo));
		if (ptr != NULL)
		{
			ps->data = ptr;
			ps->capacity += 2;
			printf("增容成功!\n");
		}
		else
		{
			printf("增容失败!\n");
		}
	}
}

// 添加联系人
void AddContact(struct Contact* ps)
{
	assert(ps);

	// 检测当前通讯录的容量
	// 1、如果满了就增加空间
	// 2、如果没满无需任何操作
	CheckCapacity(ps);
	// 增加数据
	printf("请输入名字:>");
	scanf("%s", ps->data[ps->size].name);
	printf("请输入年龄:>");
	scanf("%d", &(ps->data[ps->size].age)); // 年龄是一个整型变量 需要取地址
	printf("请输入性别:>");
	scanf("%s", ps->data[ps->size].sex);
	printf("请输入电话:>");
	scanf("%s", ps->data[ps->size].tele);
	printf("请输入地址:>");
	scanf("%s", ps->data[ps->size].addr);

	ps->size++;
	printf("添加成功!\n");
}

// 展示通讯录中的信息
void ShowContact(const struct Contact* ps)
{
	if (ps->size == 0)
	{
		printf("通讯录为空!\n"); // 判断通讯录人数是否为0

	}
	else
	{
		int i = 0;
		// 打印标题
		printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址");
		// 打印数据
		for (i = 0; i < ps->size; i++)
		{
			printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n",
				ps->data[i].name,
				ps->data[i].age,
				ps->data[i].sex,
				ps->data[i].tele,
				ps->data[i].addr);
		}
	}
}

// 通过名字查找指定联系人所在的下标
static int FindByName(const struct Contact* ps, char name[MAX_NAME])
{
	int i = 0;
	for (i = 0; i < ps->size; i++)
	{
		if (0 == strcmp(ps->data[i].name, name))
		{
			return i;
		}
	}

	return -1; // 找不到返回-1
}

// 删除指定联系人
void DelContact(struct Contact* ps)
{
	char name[MAX_NAME];
	int pos = 0;
	printf("请输入要删除人的名字:>");
	scanf("%s", name);
	// 1、查找要删除的人在什么位置
	// 找到了返回名字所在元素的下标
	// 找不到返回 -1
	pos = FindByName(ps, name);

	// 2、删除
	if (pos == -1)
	{
		printf("要删除的人不存在!\n");
	}
	else
	{
		// 删除数据
		int j = 0;
		for (j = pos; j < ps->size - 1; j++)
		{
            // 把要删除的人后面一个人的信息依次从前向后往前移动一位,覆盖掉其信息
			ps->data[j] = ps->data[j + 1];
		}
		ps->size--;
		printf("删除成功!\n");
	}
}

// 查找指定联系人信息
void SearchContact(const struct Contact* ps)
{
	int pos = 0;
	char name[MAX_NAME];
	printf("请输入要查找人的名字:>");
	scanf("%s", name);
	pos = FindByName(ps, name); // 查找
	if (pos == -1)
	{
		printf("要查找的人不存在!\n");
	}
	else
	{
		printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址");
		//数据
		printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n",
			ps->data[pos].name,
			ps->data[pos].age,
			ps->data[pos].sex,
			ps->data[pos].tele,
			ps->data[pos].addr);
	}
}

// 修改指定联系人信息
void ModifyContact(struct Contact* ps)
{
	int pos = 0;
	char name[MAX_NAME];
	printf("请输入要修改人的名字:>");
	scanf("%s", name);
	pos = FindByName(ps, name);
	if (pos == -1)
	{
		printf("要修改人的信息不存在!\n");
	}
	else
	{
		printf("请输入名字:>");
		scanf("%s", ps->data[pos].name);
		printf("请输入年龄:>");
		scanf("%d", &ps->data[pos].age);
		printf("请输入性别:>");
		scanf("%s", ps->data[pos].sex);
		printf("请输入电话:>");
		scanf("%s", ps->data[pos].tele);
		printf("请输入地址:>");
		scanf("%s", ps->data[pos].addr);

		printf("修改完成!\n");
	}
}

// 排序通讯录内容
// 比较结构体数组中两个元素的姓名成员
int compareStructType_name(const void* elem1, const void* elem2)
{
	// 姓名成员是字符串,用strcmp比较
	return strcmp(((struct PeoInfo*)elem1)->name, ((struct PeoInfo*)elem2)->name);
}

// 以名字排序所有联系人
void SortContact(struct Contact* ps)
{
	// 判断通讯录是否为空
	if (ps->size == 0)
	{
		printf("通讯录为空,无法排序!\n");
		return;
	}
	else
	{
		// 根据姓名成员对结构体数组升序排列
		qsort(ps->data, ps->size, sizeof(ps->data[0]), compareStructType_name);
		printf("以名字排序联系人成功!\n");
	}
}

// 销毁通讯录
void DestoryContact(Contact* ps)
{
	free(ps->data);
	ps->data = NULL;
	ps->capacity = 0;
	ps->size = 0;
	printf("销毁成功!\n");
}

六、程序运行效果 

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

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

相关文章

Maven之tomcat7-maven-plugin 版本低的问题

tomcat7-maven-plugin 版本『低』的问题 相较于当前最新版的 tomcat 10 而言&#xff0c;tomcat7-maven-plugin 确实看起来很显老旧。但是&#xff0c;这个问题并不是问题&#xff0c;至少不是大问题。 原因 1&#xff1a;tomcat7-maven-plugin 仅用于我们&#xff08;程序员&…

为什么企业需要内部威胁检测软件?

在数字时代&#xff0c;企业不仅需要抵御外部威胁&#xff0c;还必须密切关注内部威胁&#xff0c;因为内部因素可能对数据安全造成严重威胁。作为一款强大的内部威胁监测工具&#xff0c;ADAudit Plus 在这一领域发挥着关键作用。本文将深入探讨 ADAudit Plus 在内部威胁监测中…

AUTOSAR NvM Block的三种类型

Native NVRAM block Native block是最基础的NvM Block&#xff0c;可以用来存储一个数据&#xff0c;可以配置长度、CRC等。 Redundant NVRAM block Redundant block就是在Native block的基础上再加一个冗余块&#xff0c;当Native block失效&#xff08;读取失败或CRC校验失…

如何使用PHP开发网页定时刷新功能

如何使用PHP开发网页定时刷新功能 随着互联网的发展&#xff0c;越来越多的网站需要实时更新显示数据。而实时刷新页面是一种常见的需求&#xff0c;它可以让用户在不刷新整个页面的情况下获得最新的数据。本文将介绍如何使用PHP开发网页定时刷新功能&#xff0c;并提供代码示…

带你了解Cloudquery 安装使用功能

&#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文章可以帮助到大家&#xff0c;您的满意是我的动力&#x1f609;&#x1f609; &#x1f495;欢迎大家&#xff1a;这里是CSDN&#xff0c;我总结…

以 Java NIO 的角度理解 Netty

文章目录 前言Java NIO 工作原理Selector 的创建ServerSocketChannel 的创建ServerSocketChannel 注册 Selector对事件的处理总结 前言 上篇文章《Netty 入门指南》主要涵盖了 Netty 的入门知识&#xff0c;包括 Netty 的发展历程、核心功能与组件&#xff0c;并且通过实例演示…

剑指 Offer 60. n个骰子的点数(动态规划)

文章目录 题目描述思路分析完整代码 题目描述 把n个骰子扔在地上&#xff0c;所有骰子朝上一面的点数之和为s。输入n&#xff0c;打印出s的所有可能的值出现的概率。 你需要用一个浮点数数组返回答案&#xff0c;其中第 i 个元素代表这 n 个骰子所能掷出的点数集合中第 i 小的那…

Status 技术的兴衰:从问世到淘汰的历程

标题 Status 技术的兴衰&#xff1a;从问世到淘汰的历程摘要引言Status 问世初期优势挑战 Status 高峰期优势挑战 Status 面临挑战挑战 新兴技术的涌现优势 淘汰的原因结论 博主 默语带您 Go to New World. ✍ 个人主页—— 默语 的博客&#x1f466;&#x1f3fb; 《java 面试…

排污口水质的在线监测,实时掌握排口水质助力生态治理

水是生命之源&#xff0c;良好的水生态环境是社会发展的必然要求。然而随着工业化和城市化的发展&#xff0c;人类面临空气和水环境污染等严峻挑战&#xff0c;其中水环境问题尤为突出。排污成为城市和工业生产过程中不可避免的环保问题。 为加快解决生态环境突出问题&#xff…

搞科研需要具备哪些特质?

你认为科研人员需要具备哪些特质才能实现成为大科学家的梦想&#xff1f;回顾那些成功科学家的经历&#xff0c;发现他们身上总会一些共性&#xff0c;正是拥有这些共性&#xff0c;他们最后都在领域内取得了成功。那么&#xff0c;到底都具备哪些共性呢&#xff1f; 好奇心。小…

手把手教你如何挑选适合你的AI编程辅助工具

ChatGPT的普及引发了对AI应用场景的探索和实践的重大变革&#xff0c;垂直领域专业类大模型正正在不断深化落地&#xff0c;其中基于大模型专注于编码的AI工具的出现显著降低了开发的复杂性&#xff0c;提高了开发人员的开发效率&#xff0c;对初级开发人员尤其有帮助。 因此面…

面试官三连问:给我讲讲什么是索引覆盖?什么是索引下推 ? 什么是最左匹配原则 ?

1.什么是索引覆盖 索引覆盖是指在一个查询语句中&#xff0c;某个索引已经 "覆盖了" 需要被查询出来的列&#xff0c;此时就不需要进行回表查询了&#xff0c;这就叫做索引覆盖&#xff01;&#xff01;&#xff08;索引覆盖它是非聚簇索引中的一个特殊情况&#xff…

3.2.0 版本预告!远程日志解决 Worker 故障获取不到日志的问题

Apache DolphinScheduler 3.2.0 版本已经呼之欲出&#xff0c;8 月 中下旬&#xff0c;这个大版本就要和用户见面了。为了让大家提前了解到此版本更新的主要内容&#xff0c;我们已经制作了几期视频和内容做了大致介绍&#xff0c;包括《重磅预告&#xff01;Apache DolphinSch…

快递再多也不怕!你的顺丰快递用上5G“神器”

互联网时代&#xff0c;剁手党疯狂“买买买”之后&#xff0c;快递件量再创新高。《2023年6月中国快递发展指数报告》显示&#xff0c;2023二季度单月快递业务量稳定在百亿件以上。其中&#xff0c;由于“618”电商促销活动与父亲节叠加&#xff0c;6月16日至20日单日揽收量均超…

js ajax 国内快速 映像

ajax 快速 映像 https://www.bootcdn.cn/ axios入门和axios基本请求方式 https://blog.csdn.net/m0_68997646/article/details/127438174 使用 jsDelivr CDN: <script src"https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>因为我们国…

ThinkPHP6.0+ 使用Redis 原始用法

composer 安装 predis/predis 依赖&#xff0c;或者安装php_redis.dll的扩展。 我这里选择的是predis/predis 依赖。 composer require predis/predis 进入config/cache.php 配置添加redis缓存支持 示例&#xff1a; <?php// -----------------------------------------…

【文件上传】大文件分片上传、断点续传、秒传前后端实现

1、大文件上传面临的问题&#xff1a; 在传统的文件上传中&#xff0c;由于文件过大&#xff0c;导致网络传输时间长&#xff0c;这过程中会遇到网络不稳定或者不小心关闭的浏览器&#xff08;电脑&#xff09;的情况&#xff0c;从而导致文件上传中断。中断之后&#xff0c;又…

第三届OceanBase数据库大赛启动,升级为国家级竞赛

近日&#xff0c;第三届OceanBase数据库大赛启动报名。本届大赛进一步升级为全国大学生计算机系统能力大赛&#xff0c;由系统能力培养研究专家组发起&#xff0c;全国高等学校计算机教育研究会、系统能力培养研究项目发起高校主办&#xff0c;OceanBase承办&#xff0c;旨在培…

API接口开发系列,各电商(商品详情数据,商品sku数据,商品属性数据,商品优惠券数据)

API接口开发是指开发和构建应用程序之间交流的接口。主要包括设计、实现和维护接口&#xff0c;以便不同应用程序之间可以进行数据交换和相互调用功能。通过API接口开发&#xff0c;可以实现不同系统、平台或服务之间的数据交互和功能集成。 API接口开发的步骤一般包括以下几个…

程序员当累了,带爱人出去广西北海旅游下吧

先说说消费吧&#xff0c;我们一共花了8000多块钱。 两个人坐的往返机票&#xff0c;3600块钱。 上涠洲岛的船票加门票890块钱 住宿费一共4天850&#xff0c; 北海 两天350&#xff0c;涠洲岛住两天400。 路费住宿5500(200滴滴打车费) 其他消费2500 好吧&#xff0c;现在开始我…