【C语言】动态内存管理基础知识——动态通讯录,如何实现通讯录容量的动态化

news2025/2/4 23:47:10

引言

动态内存管理的函数有:malloc,calloc,ralloc,free,本文讲解动态内存函数和使用,如何进行动态内存管理,实现通讯录联系人容量的动态化,对常见动态内存错误进行总结。

        

 158c3f50b199454985017a51dbef9841.png               ✨ 猪巴戒:个人主页✨

               所属专栏:《C语言进阶》

        🎈跟着猪巴戒,一起学习C语言🎈

目录

引言

为什么存在动态内存分配

malloc

动态内存空间位置

 内存泄漏问题

free

calloc   

realloc 

realloc如何开辟动态内存空间

动态版本的通讯录

常见动态内存错误

1.对NULL空指针进行解引用

2.对动态开辟空间的越界访问

3.对非动态开辟的内存使用free释放

4.使用free释放动态开辟内存的一部分

5.对同一块动态内存多次释放

6.动态开辟内存忘记释放(内存释放)

动态通讯录的实现



为什么存在动态内存分配

int main()
{
    int arr[20] = {0};//整形数组开辟20个元素,就是80个字节。
    return 0;
}

1.空间开辟大小是固定的

2.数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配。

如果我们开辟的空间不够,那么进行修改会比较麻烦。如果开辟的空间较大,那么所占据的空间又会很大。那么有没有一种方法可以用多少空间就开辟多大的内存呢?

这个时候就有了动态内存开辟。


malloc

动态内存函数的头文件  <stdlib.h>

void* malloc (size_t size)

a066e21521c34f6e966d7c58c5b1d1fb.png

申请内存块,

size

就是我们要申请的字节大小。当然,我们的内存是有限的,不是想要申请多少内存就可以申请多少内存。

返回值

  • 如果开辟成功,则返回一个指向开辟好空间的指针。
  • 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
  • 返回值的类型是void*,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。

开创的空间放回地址是void*类型,使用的时候记得要强制类型转换。

所以我们在使用malloc函数时,要检测返回值p是否是空指针。

#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include<stdio.h>
int main()
{
	int arr[10] = { 0 };
	//动态内存开辟
	int* p = (int*)malloc(40);
	if (p == NULL)      //检测返回的p是不是空指针。
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	//使用动态内存
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(p + i) = i;
	}
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
	}
	//释放
	free(p);    //常常搭配malloc、calloc、realloc使用,后面会讲到
	p = NULL;
	return 0;
}

f8d11f8a978c45c9a26e6b479d12acb3.png

         

动态内存空间位置

 动态内存函数是在堆区开辟内存空间的。我们一般的局部变量、形式参数都是存放在栈区。

2a755d6571ce4bc28cdd42725109c2d3.png

         

 内存泄漏问题

什么是内存泄漏?

我们创建的局部变量,数据会在函数结束时释放。

动态内存空间存放临时使用的数据,这些数据不必等到函数结束时释放,而是需要时随时开辟,不需要时,随时释放。动态开辟的内存使用完是要进行释放的,如果不对内存进行释放,那么开辟的动态内存就会被之前的数据占据,这部分的内存就无法使用,相当于丢失了内存。因此我们把这类问题叫做内存泄漏

下面的代码如果吧进行释放,那么就会一直占据内存空间。

858d90982a85432cbc47bdc0798a0ef1.png

 通过释放动态内存,内存可以被重新调用

9a8aa7cd08754d5db41e8a501d5ee008.png

最后我们加了一步,p = NULL,p的内存空间已经被释放了还给操作系统了,但是p还是原来的地址,我们通过p就会使用到一个已经释放的内存,这就会导致野指针问题。为了避免这种情况,我们将p的地址去掉就可以了。

17cef251579d40c797c86392201a90f2.png

        

free

void free (void* ptr)

free函数是专门用来释放动态开辟的内存。

  • 如果参数ptr指向的空间不是动态开辟的,那free函数的行为是未定义的。
  • 如果参数ptr 是NULL指针,则函数什么事都不做。

free函数只能释放动态内存开辟的空间,如果释放其他空间,就会报错。

#include <stdlib.h>
int main()
{
	int a = 0;
	int* p = &a;
	free(p);
	p = NULL;
	return 0;
}

da583dd28bf24bd482fa2e8067931150.png

        

calloc   

void* calloc (size_t num, size_t size)

f79aadb251314d11839bd0058f502487.png

num 

开辟空间元素的个数

size

空间中每个元素的大小。

  •  函数的功能为num个大小为size 的元素开辟一块空间,并且把空间的每个字节初始化为0
  • callocmalloc的区别在于calloc在放回地址之前把申请的空间的每个字节初始化为全0。

下面的例子就是解释:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{
	int* p = (int*)calloc(10, sizeof(int));
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
	}
	//打印
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
	}
	//释放
	free(p);
	p = NULL;
	return 0;
}

a523a57b3c8a4aae938562d1611e95f6.png

        

realloc 

void* realloc (void* ptr, size_t size)

2843916f62db4e2b8fceee1671efeb18.png

ptr

要调整的空间的起始位置。

size

  • realloc函数的出现让动态内存管理更加灵活。
  • 有时我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间豁达了,那为了合理的内存,我们一定对内存的大小做灵活的调整。那realloc函数可以做到对动态开辟内存大小的调整。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
	}
	//使用
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(p + i) = i + 1;
	}
	//扩容
	int* ptr =  realloc(p, 80);
	if (ptr != NULL)  //这里不直接将新的地址赋给p,是因为realloc有可能开辟失败返回空指针。后面详细讲
	{
		p = ptr;
	}
	for(i=0;i<10;i++)
	{
		printf("%d ", *(p + i));
	}
	//释放
	free(p);
	p = NULL;
	return 0;
}

88f1530abbf448fbb5a1f45dc2e8873f.png

        

realloc如何开辟动态内存空间

realloc在调整内存空间的是存在两种情况。

第一种情况,原空间之后有足够大的空间。当要进行调整的内存空间后面有多余的40个字节空间,那么就可以直接开辟向后面开辟40个字节的空间,然后放回起始位置的地址,这里指的是0的地址。

5de786ff5e05407c92d31f2e1c575847.png

698f0ff7ee3441a580545c584564081e.png

第二种情况原空间之后没有足够大的空间。要调整的内存空间后面不足以存放40个字节的空间,那么就要重新找到一个新的地址(可以存放80个字节)开辟,开辟成功后返回起始位置的地址。

注意:如果需要开辟的空间过大,是会开辟失败的,开辟失败,realloc返回空指针,所以要检查空指针。

这里把realloc开辟的空间换成8000,来实现第二种情况。

1ad74892b124494c959288e26152895b.png e2a52b304f504866a715c4e5bcbbefb8.pngf4bc4b17e6db40799be54b0eb0d8fc7e.png

         

动态版本的通讯录

动态:

要实现通讯录容量的动态化,要实现两个功能

1.通讯录默认能存放3个人的信息

2.如果空间不够了,就增加空间,每次增加2个人的空间

静态:

原来的通讯录的信息由结构体组成的数组,来存放信息,设置的是100人的信息。

1.当没有100人的信息时,会造成空间上的浪费。

2.当超过100人的信息时,又无法自动扩容,如果想要扩容要手动改变最大存放的空间。

原通讯录的代码:

有兴趣可以学习通讯录的实现http://t.csdnimg.cn/UbT9I

 存放数据的改变

首先把存放联系人信息的结构体给大家看看

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

原来存放联系人的信息是通过数组data[100],这样的数组的空间是固定的,是一开始设置的最大容量。

typedef struct Contact
{
	PeoInfo data[MAX];
	int count;//记录当前通讯录中实际人数的个数
}Contact;

不以数组的形式进行存放,以数组改成地址,但是地址也可以像数组一样访问数据。data是联系人的起始地址,如果想要访问第二个人,就是*(data+1),等于data[1]。

count是当前使用了多少个联系人的空间.

capacity记录的是现在存放联系人的容量是多大。因为当每次容量不够时,就增加空间,每次增加2个人的空间。也就是每次count == capacity的时候,capacity就要加2.

typedef struct Contact
{
	PeoInfo* data;
	int count;//记录当前通讯录中实际人数的个数
	int capacity;
}Contact;

1.初始化的改变

assert函数,用来检验空指针,如果为空指针,就会报错。

参数pc,是创立的struct Contact结构体变量的地址,这里是传址调用,作用就是改变原来的数据。

原通讯录是存放100个联系人的数组,将100个联系人的数据初始化为0。

void InitContact(Contact* pc)
{
	assert(pc);
	pc->count = 0;
	memset(pc->data, 0, sizeof(pc->data));
}

动态通讯录要实现开辟3个联系人的空间,并将它们进行初始化。 既要开辟空间,又要进行初始化,我们想到calloc函数。

malloc是单纯地开辟空间,realloc是既开辟空间,并进行初始化。mallocrealloc地区别就在于是否对开辟的空间初始化。

开辟3个联系人空间,calloc进行开辟,将地址传给pc->data.将记录联系人的容量传给capacity.

int InitContact(Contact* pc)
{
	assert(pc);
	pc->count = 0;
	pc->data =(int*) calloc(3, sizeof(PeoInfo));
	if (pc == NULL)
	{
		printf("InitContact::%s\n", strerror(errno));
		return 1;
	}
	pc->capacity = 3;
	return 0;
}

 2.增加联系人

参数pc,是创立的struct Contact结构体变量的地址,这里是传址调用,作用就是改变原来的数据。

动态内存管理,通过pc->data[count]可以进行数据的输入。最重要的是实现通讯录容量的动态化。

count表示已经使用的联系人数量,capacity表示联系人的总容量。

当count == capacity时,就要动态开辟内存,对容量进行增容。

CheckCapacity为自定义增容函数,我们要实现增容的功能。

realloc重新开辟内存块,实现内存的动态化。realloc返回的起始地址不能直接传给data,因为动态内存的开辟有可能失败,失败传回空指针。

capacity增加2,最后提示增容成功。

void CheckCapacity(Contact* pc)
{
	if (pc->count == pc->capacity);
	{
		PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + 2) * sizeof(PeoInfo));
		if (ptr == NULL)
		{
			printf("AddContact::%s\n", strerror(errno));
			return 1;
		}
		else
		{
			pc->data = ptr;
			pc->capacity += 2;
			printf("增容成功\n");
		}
	}
}
void AddContact(Contact* pc)
{
	assert(pc);
	//增容
	CheckCapacity(pc);

	printf("请输入名字:》");
	scanf("%s", pc->data[pc->count].name);
	printf("请输入年龄:》");
	scanf("%d", &(pc->data[pc->count].age));
	printf("请输入性别:>");
	scanf("%s", pc->data[pc->count].sex);
	printf("请输入电话:>");
	scanf("%s", pc->data[pc->count].tele);
	printf("请输入地址:>");
	scanf("%s", pc->data[pc->count].addr);

	pc->count++;
	printf("增加成功\n");
}

将整体的代码呈现在文章末尾。

        

常见动态内存错误

        

1.对NULL空指针进行解引用

如果开辟的空间过大,malloc有可能开辟失败,开辟失败就会返回空指针。如果直接对p进行解引用,就会产生问题。

#include <stdlib.h>
int main()
{
	int* p = (int*)malloc(40);
	*p = 20;
	return 0;
}

正确解决方法: 

在开辟动态内存后对p进行检验,是否为空指针。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
        printf("%s\n", strerror(errno));
		return 1;
	}
	*p = 20;
	free(p);
	p = NULL;
	return 0;
}

        

2.对动态开辟空间的越界访问

我们只开辟了10个字节的空间,但是访问,从0到10,10算进去的话就是11个元素,这里访问越界了,就会出问题。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	//使用
	int i = 0;
	for (i = 0; i <= 10;i++)
	{
		p[i] = i;
	}
	free(p);
	p = NULL;
	return 0;
}

正确解决方法: 

一定要注意我们开辟的空间是否和访问的空间是一样的。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	//使用
	int i = 0;
	for (i = 0; i < 10;i++)
	{
		p[i] = i;
	}
	free(p);
	p = NULL;
	return 0;
}

        

3.对非动态开辟的内存使用free释放

free只能够释放动态开辟的内存,不能够随意去使用函数。

#include <stdlib.h>
int main()
{
	int a = 10;
	int* p = &a;
	//....

	free(p);
	p = NULL;
	return 0;
}

ec2893e717184b06857487260a7a58de.png

        

4.使用free释放动态开辟内存的一部分

free做不到释放动态内存的一部分,如果要释放要将整个动态内存进行释放。

#include <stdlib.h>
int main()
{
	int* p = (int*)malloc(40);
	if (p = NULL)
	{
		return 1;
	}

	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*p = i;
		p++;
	}
	free(p);
	p = NULL;
	return 0;
}

正确解决方法: 

不改变p的位置,对p进行释放。使用p的话,通过:

*(p+i) = i;

        

5.对同一块动态内存多次释放

free的二次使用,第二次使用的动态内存空间已经还给操作系统了,但是还能对p进行操作,就是野指针问题。

#include <stdlib.h>
int main()
{
	int* p = (int*)malloc(40);
	free(p);
	//....
	free(p);
	return 0;
}

正确解决方法: 

避免free的二次使用,或者将p转化为空指针。

#include <stdlib.h>
int main()
{
	int* p = (int*)malloc(40);
	free(p);
	p = NULL;
	free(p);
	return 0;
}

        

6.动态开辟内存忘记释放(内存释放)

看看下面的例子:

如果flag = 5的话,那么后面free函数就会跳过,动态开辟的内存就不能释放。

#include <stdio.h>
#include <stdlib.h>
void test()
{
	int* p = (int*)malloc(100);
	int flag = 0;
	scanf("%d", &flag);
	if (flag == 5)
	{
		return;
	}
	free(p);
	p = NULL;
}
int main()
{
	test();
	return 0;
}

动态通讯录的实现

 contact.h

头文件:用来对函数的声明

#pragma once

#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>

#define DEFAULT_SZ 3
#define INC_SZ 2
#define MAX 100
#define MAX_NAME 20
#define MAX_SEX 10
#define MAX_TELE 12
#define MAX_ADDR 30

//类型的声明
typedef struct PeoInfo
{
	char name[MAX_NAME];
	int age;
	char sex[MAX_SEX];
	char tele[MAX_TELE];
	char addr[MAX_ADDR];
}PeoInfo;

typedef struct Contact
{
	PeoInfo* data;
	int count;//记录当前通讯录中实际人数的个数
	int capacity;
}Contact;

//初始化通讯录
int InitContact(Contact* pc);

//销毁通讯录
void DestroyContact(Contact* pc);

//增加联系人到通讯录
void AddContact(Contact* pc);

//打印通讯录中信息
void ShowContact(const Contact* pc);

//删除联系人的信息
void DelContact(Contact* pc);

//查找指定联系人
void SeachContact(Contact* pc);

//修改指定联系人
void ModifyContact(Contact* pc);

//排序通讯录中的内容
//按照名字来排序
void SortContact(Contact* pc);

contact.c

源文件:函数的实现和定义

#define _CRT_SECURE_NO_WARNINGS

#include "contact.h"

//动态版本
int InitContact(Contact* pc)
{
	assert(pc);
	pc->count = 0;
	pc->data =(int*) calloc(DEFAULT_SZ, sizeof(PeoInfo));
	if (pc == NULL)
	{
		printf("InitContact::%s\n", strerror(errno));
		return 1;
	}
	pc->capacity = DEFAULT_SZ;
	return 0;
}

void DestroyContact(Contact* pc)
{
	assert(pc);
	free(pc->data);
	pc->data = NULL;
}

void CheckCapacity(Contact* pc)
{
	if (pc->count == pc->capacity);
	{
		PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo));
		if (ptr == NULL)
		{
			printf("AddContact::%s\n", strerror(errno));
			return 1;
		}
		else
		{
			pc->data = ptr;
			pc->capacity += INC_SZ;
			printf("增容成功\n");
		}
	}
}
void AddContact(Contact* pc)
{
	assert(pc);
	//增容
	CheckCapacity(pc);

	printf("请输入名字:》");
	scanf("%s", pc->data[pc->count].name);
	printf("请输入年龄:》");
	scanf("%d", &(pc->data[pc->count].age));
	printf("请输入性别:>");
	scanf("%s", pc->data[pc->count].sex);
	printf("请输入电话:>");
	scanf("%s", pc->data[pc->count].tele);
	printf("请输入地址:>");
	scanf("%s", pc->data[pc->count].addr);

	pc->count++;
	printf("增加成功\n");
}

void ShowContact(const Contact* pc)
{
	assert(pc);
	int i = 0;
	//一个汉字是两个字符
	printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "名字", "年龄", "性别", "电话", "地址");
	for (i = 0; i < pc->count; i++)
	{
		printf("%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n", pc->data[i].name,
			pc->data[i].age,
			pc->data[i].sex,
			pc->data[i].tele,
			pc->data[i].addr);

	}
}


static int FindByName(Contact* pc, char name[])
{
	assert(pc);
	int i = 0;
	for (i = 0; i < pc->count; i++)
	{
		if (0 == strcmp(pc->data[i].name, name))
		{
			return i;
		}

	}
	return -1;
}

void DelContact(Contact* pc)
{
	char name[MAX_NAME] = { 0 };
	assert(pc);
	int i = 0;
	if (pc->count == 0)
	{
		printf("通讯录为空,没有信息可以删除\n");
		return;
	}
	printf("请输入要删除人的名字:>");
	scanf("%s", name);

	//删除
	//1.查找
	int pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("要删除的人不存在\n");
		return;
	}
	//2.删除
	for (i = pos; i < pc->count; i++)
	{
		pc->data[i] = pc->data[i + 1];
	}
	pc->count--;
}

void SeachContact(Contact* pc)
{
	assert(pc);
	char name[MAX_NAME] = { 0 };
	printf("请输入需要查找的联系人的名字:>");
	scanf("%s", name);
	//1.查找
	int pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("要查找的人不存在\n");
		return;
	}
	//2.打印
	printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "名字", "年龄", "性别", "电话", "地址");
	printf("%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n", pc->data[pos].name,
		pc->data[pos].age,
		pc->data[pos].sex,
		pc->data[pos].tele,
		pc->data[pos].addr);


}

void ModifyContact(Contact* pc)
{
	assert(pc);
	char name[MAX_NAME] = { 0 };
	printf("请输入需要查找的联系人的名字:>");
	scanf("%s", name);
	//1.查找
	int pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("要查找的人不存在\n");
		return;
	}
	printf("要修改人的信息已经找到,接下来进行修改\n");
	//2.修改
	printf("请输入名字:》");
	scanf("%s", pc->data[pos].name);
	printf("请输入年龄:》");
	scanf("%d", &(pc->data[pos].age));
	printf("请输入性别:>");
	scanf("%s", pc->data[pos].sex);
	printf("请输入电话:>");
	scanf("%s", pc->data[pos].tele);
	printf("请输入地址:>");
	scanf("%s", pc->data[pos].addr);

	printf("修改成功\n");
}

int cmp_peo_by_name(const void* e1, const void* e2)
{
	return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}
//按照名字来排序
void SortContact(Contact* pc)
{
	assert(pc);
	qsort(pc->data, pc->count, sizeof(PeoInfo), cmp_peo_by_name);
	printf("排序成功\n");
}

test.c

通讯录主脉络:

#define _CRT_SECURE_NO_WARNINGS

#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;
	Contact con;
	//初始化通讯录:模块化初始化
	InitContact(&con);//只能传地址,进行修改
	do
	{
		menu();
		printf("请选择:》");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			AddContact(&con);
			break;
		case 2:
			DelContact(&con);
			break;
		case 3:
			SeachContact(&con);
			break;
		case 4:
			ModifyContact(&con);
			break;
		case 5:
			ShowContact(&con);
			break;
		case 6:
			SortContact(&con);
			break;
		case 0:
			DestroyContact(&con);
			printf("退出通讯录\n");
			break;
		default:
			printf("选择错误\n");
		}
	} while (input);
	return 0;
}

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

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

相关文章

【大模型】快速体验百度智能云千帆AppBuilder搭建知识库与小助手

文章目录 前言千帆AppBuilder什么是千帆AppBuilderAppBuilder能做什么 体验千帆AppBuilderJava知识库高考作文小助手 总结 前言 前天&#xff0c;在【百度智能云智算大会】上&#xff0c;百度智能云千帆AppBuilder正式开放服务。这是一个AI原生应用开发工作台&#xff0c;可以…

业务逻辑漏洞有哪些?漏洞攻击防御及代码示例

文章目录 简介危害成因攻击防御代码示例1. 未经验证的重要操作2. 认证绕过3. 逻辑时间窗口漏洞4. 负载测试漏洞 修复 业务逻辑漏洞是指软件或系统的逻辑设计上的缺陷&#xff0c;这些缺陷可能被攻击者利用&#xff0c;从而导致意料之外的行为。下面是对业务逻辑漏洞的简介、危害…

大数据技术基本功-数据采集

产品指南&#xff5c;DataScale自定义采集器功能介绍产品指南&#xff5c;开发 DataScale Collector​​​​​​​

ubuntu换源

1 首先备份Ubuntu源列表 sudo cp /etc/apt/sources.list /etc/apt/sources.list.backup 2 查看自己Ubuntu版本 命令 lsb_release -a precise为源里面的关键字,根据实际情况,自行修改 3 修改更新源 先删除原文件里面的内容 sudo gedit /etc/apt/sources.list 用下面内容替…

哈希表..

文章目录 1. 两数之和-力扣 1 题 1. 两数之和-力扣 1 题 思路&#xff1a; 循环遍历数组&#xff0c;拿到每个数字x以target-x作为key到map中查找 若没找到&#xff0c;将x 作为key&#xff0c;它的索引作为value 存入map 若找到了&#xff0c;返回 x 和它配对数的索引即可 …

【让云服务器更灵活】iptables转发tcp/udp端口请求

iptables转发tcp/udp端口请求 文章目录 前言一、路由转发涉及点二、转发如何配置本机端口转发到本机其它端口本机端口转发到其它机器 三、固化iptables总结 前言 路由转发是计算机网络中的一种重要概念&#xff0c;特别是在网络设备和系统之间。它涉及到如何处理和传递数据包&…

Redis(非关系型数据库)

Redis(非关系型数据库) 文章目录 Redis(非关系型数据库)认识Redis(Remote Dictionary Server)1.Redis的基本介绍2.Redis的应用场景2.1 取最新N个数据的操作2.2 排行榜应用,取TOP N操作2.3 需要精准设定过期时间的应用2.4 计数器应用2.5 Uniq 操作&#xff0c;获取某段时间所有数…

爬虫API|批量抓取电商平台商品数据,支持高并发

随着互联网的快速发展&#xff0c;电商平台如雨后春笋般涌现&#xff0c;为消费者提供了丰富的购物选择。然而&#xff0c;对于许多商家和数据分析师来说&#xff0c;如何快速、准确地获取电商平台上的商品数据成为了一个难题。为了解决这个问题&#xff0c;我们开发了一个爬虫…

GBASE南大通用数据库在Windows和Linux中创建数据源

Windows 中数据源信息可能存在于两个地方&#xff1a;在 Windows 注册表中&#xff08;对 Windows 系统&#xff09;&#xff0c; 或在一个 DSN 文件中&#xff08;对任何系统&#xff09;。 如果信息在 Windows 注册表中&#xff0c;它叫做“机器数据源”。它可能是一个“用 …

基于若依的ruoyi-nbcio流程管理系统增加待办通知个性化设置

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 1、在每个节点可以设置扩展属性是todo的属性值&#xff0c;如下&#xff1a; 2、在需要审批或启动的时候获…

华为鸿蒙操作系统简介及系统架构分析(2)

接前一篇文章&#xff1a;华为鸿蒙操作系统简介及系统架构分析&#xff08;1&#xff09; 本文部分内容参考&#xff1a; 鸿蒙系统学习笔记(一) 鸿蒙系统介绍 特此致谢&#xff01; 上一回对于华为的鸿蒙操作系统&#xff08;HarmonyOS&#xff09;进行了介绍并说明了其层次化…

韵达快递查询入口,一键将退回件筛选出来

批量查询韵达快递单号的物流信息&#xff0c;并将退回件一键筛选出来。 所需工具&#xff1a; 一个【快递批量查询高手】软件 韵达快递单号若干 操作步骤&#xff1a; 步骤1&#xff1a;运行【快递批量查询高手】软件&#xff0c;并登录 步骤2&#xff1a;点击主界面左上角的…

基于ssm高校实验室信息化综合管理平台建设系统论文

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本高校实验室信息化综合管理就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的…

探索元宇宙:UTONMOS带来前所未有的游戏体验

你是否曾经梦想过进入一个完全虚拟的世界&#xff0c;在那里你可以自由地探索、创造和与其他玩家互动&#xff1f; UTONMOS元宇宙游戏将这一梦想变为现实&#xff0c;为你带来前所未有的游戏体验。 UTONMOS旗下元宇宙游戏《神念无界-源起山海》是一种基于虚拟现实、区块链、元…

OpenHarmony 4.0 Release发布,同步升级API 10

不久之前&#xff0c;OpenHarmony 正式发布了4.0 版本&#xff0c;开发套件也同步升级到 API 10。相比 3.2 Release 版本&#xff0c;4.0 版本新增 4000 多个 ArkTS API&#xff0c;应用开发能力更加丰富&#xff1b;HDF 新增 200 多个 HDI 接口&#xff0c;硬件适配更加便捷&a…

京东tp26旋转验证

记录一下&#xff0c;狗东的tp26旋转验证码&#xff0c;难点还是在这个轨迹上。我真的是一点都不喜欢玩轨迹&#xff01;&#xff01;&#xff01;&#xff01; 类似于百度旋转的图&#xff0c;不过他这个东西还是稍微有点差距的。 鉴于生病了脑子不太好使&#xff0c;就不过多…

大型语言模型:SBERT — Sentence-BERT

slavahead 一、介绍 Transformer 在 NLP 方面取得了进化进步&#xff0c;这已经不是什么秘密了。基于转换器&#xff0c;许多其他机器学习模型已经发展起来。其中之一是BERT&#xff0c;它主要由几个堆叠的变压器编码器组成。除了用于情感分析或问答等一系列不同的问题外&#…

JavaOOP篇----第十一篇

系列文章目录 文章目录 系列文章目录前言一、Static关键字有什么作用?二、final在java中的作用,有哪些用法?三、StringString StringBuffffer 和 StringBuilder 的区别是什么?四、String str=”aaa”,与String str=new String(“aaa”)一样吗?前言 前些天发现了一个巨牛的…

Vanilla Pro for Mac 一款隐藏菜单栏图标工具

Vanilla Pro Vanilla Pro是一款简单易于使用的Mac应用程序&#xff0c;可让您隐藏菜单栏图标。只需下载Vanilla&#xff0c;启动应用程序&#xff0c;然后按照提示即可开始。 资源获取 Vanilla Pro for Mac 功能特性 键盘快捷键&#xff1a;设置自定义键盘快捷键来切换菜单…

算法时间复杂度和空间复杂度

算法&#xff0c;即解决问题的方法。同一个问题&#xff0c;使用不同的算法&#xff0c;虽然得到的结果相同&#xff0c;但是耗费的时间和资源是不同的。 就比如要拧一个螺母&#xff0c;使用扳手还是钳子是有区别的&#xff0c;虽然使用钳子也能拧螺母&#xff0c;但是没有扳…