实验五 内存管理实验
实验五 内存管理实验
一、实验目的
1、了解操作系统动态分区存储管理过程和方法。
2、掌握动态分区存储管理的主要数据结构--空闲表区。
3、加深理解动态分区存储管理中内存的分配和回收。
4、掌握空闲区表中空闲区3种不同放置策略的基本思想和实现过程。
5、通过模拟程序实现动态分区存储管理。
6、了解请求分页虚拟存储管理技术的方法和特点。
7、通过模拟实现请求页式存储管理的几种基本页面置换算法。
8、掌握页面置换算法种缺页率、置换率和命中率的计算方法。
二、实验内容
1、编程模拟实现动态分区管理中内存的分配和回收及空闲区表的管理。(2分)
首次适应算法(First Fit)参考程序:
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#define N 5
struct freearea
{
int startaddr; //空闲区起始地址
int size; //空闲区大小
int state; //1表示空闲,0表示占用
}freeblock[N]={{20,20,1},{80,50,1},{150,100,1},{300,30,1},{600,100,1}};
int alloc(int size)
{
int i,tag=0,allocaddr;
for(i=0;i<N;i++)
{
if(freeblock[i].state==1 && freeblock[i].size>size) //申请空间小于空闲空间
{
allocaddr=freeblock[i].startaddr; //作业地址
freeblock[i].startaddr+=size; //新空闲区起始地址
freeblock[i].size-=size; //新空闲区大小
tag=1; //分配标记
break;
}
else if(freeblock[i].state==1 && freeblock[i].size==size)//申请空间正好等于空闲空间
{
allocaddr=freeblock[i].startaddr;
freeblock[i].state=0;tag=1;
break;
}
}
if(tag==0) //表示没找到合适的空闲区,未分配内存
allocaddr=-1;
return allocaddr; //返回作业地址
}
void setfree(int addr,int size) //传过来的参数是要释放内存的起始地址和大小
{
int i,tag1=0,tag2=0,n1=0,n2=0;
for(i=0;i<N;i++)
{
if(freeblock[i].startaddr+freeblock[i].size==addr && freeblock[i].state==1)
{
tag1=1; //有上邻标记
n1=i; //记录上邻数组位置(分区号)
break;
}
}
for(i=0;i<N;i++)
{
if(freeblock[i].startaddr==addr+size && freeblock[i].state==1)
{
tag2=1; //有下邻标记
n2=i; //记录下邻数组位置(分区号)
break;
}
}
if(tag1==1 && tag2==0) //有上邻无下邻
{
freeblock[n1].size+=size;
}
else if(tag1==1 && tag2==1) //有上邻有下邻
{
freeblock[n1].size+=freeblock[n2].size+size;
freeblock[n2].state=0;//???
}
else if(tag1==0 && tag2==1) //无上邻有下邻
{
freeblock[n2].startaddr=addr;
freeblock[n2].size+=size;
}
else //无上邻无下邻(表明空间正好全部分配出去,空间的状态为0)
{
for(i=0;i<N;i++)
{
if(freeblock[i].state==0) //通过空间状态值找到这块空间
{
freeblock[i].startaddr=addr;
freeblock[i].size=size;
freeblock[i].state=1;
break;
}
}
}
}
void adjust()
{
int i,j;
struct freearea temp;
for(i=1;i<N;i++) //依据首次适应算法将空闲区按起始地址由低到高冒泡排序
{
for(j=0;j<N-i;j++)
{
if(freeblock[j].startaddr>freeblock[j+1].startaddr)
{
temp=freeblock[j];
freeblock[j]=freeblock[j+1];
freeblock[j+1]=temp;
}
}
}
for(i=1;i<N;i++) //把状态为0的排到后面
{
for(j=0;j<N-i;j++)
{
if(freeblock[j].state==0 && freeblock[j+1].state==1)
{
temp=freeblock[j];
freeblock[j]=freeblock[j+1];
freeblock[j+1]=temp;
}
}
}
}
void print()
{
int i;
printf("\t|-------------------------------|\n");
printf("\t|startaddr size state |\n");
for(i=0;i<N;i++)
printf("\t|%4d %4d %4d |\n",freeblock[i].startaddr,freeblock[i].size,freeblock[i].state);
}
int main()
{
int size,addr;
char c1,c2,c;
printf("At first the free memory is this:\n");//首先,空闲区是这样的
adjust();
print();
printf("Is there any job request memory?(y or n):");//有作业需要申请内存么?
while((c1=getchar())=='y')
{
printf("Input request memory size:"); //输入所需内存大小
scanf("%d",&size);
addr=alloc(size); //调用内存分配函数,返回的是作业的起始地址
if(addr==-1)
printf("There is no fit memory.Please wait!!!\n");
else
{
printf("Job's memory start address is:%d\n",addr); //输出作业的起始地址
printf("Job's size is:%d\n",size); //输出作业大小
printf("After allocation the free memory is this:\n"); //分配后可用内存如下: adjust();
print();
printf("Job is running.\n");
}
getchar();
printf("Is there any memory for free?(y or n):");//有需要释放的内存么?
while((c2=getchar())=='y')
{
printf("Input free area startaddress:");
scanf("%d",&addr); //输入要释放内存的起始地址
printf("Input free area size:");
scanf("%d",&size); //输入要释放内存的大小
setfree(addr,size); //调用释放内存函数
adjust();
print();
getchar();
printf("Is there any memory for free?(y or n):");
}
getchar();
printf("Is there any job request memory?(y or n):");
}
return 0;
}
运行结果截屏(包含分配和回收两部分):

分析该程序,列出各模块实现的功能:
- alloc()
根据作业的大小申请空闲内存区域,并返回作业的起始地址。
2)setfree()
释放指定起始地址和大小的内存区域,将其设置为空闲状态。
3) adjust()
根据首次适应算法对空闲区进行排序和整理。
- 修改上题,用最佳适应算法和最坏适应算法模拟内存空间的分配和回收。(4分)
注:只需列出程序不同部分,无需将整个程序列出。
- 最佳适应算法
int best_fit_alloc(int size) {
int i, allocaddr = -1, min_size = INT_MAX;
for (i = 0; i < N; i++) {
if (freeblock[i].state == 1 && freeblock[i].size >= size && freeblock[i].size < min_size) {
allocaddr = freeblock[i].startaddr;
min_size = freeblock[i].size;
}
}
if (allocaddr != -1) {
for (i = 0; i < N; i++) {
if (freeblock[i].startaddr == allocaddr) {
if (freeblock[i].size > size) {
freeblock[i].startaddr += size;
freeblock[i].size -= size;
} else {
freeblock[i].state = 0;
}
break;
}
}
}
return allocaddr;
}
- 最坏适应算法
int worst_fit_alloc(int size) {
int i, allocaddr = -1, max_size = -1;
for (i = 0; i < N; i++) {
if (freeblock[i].state == 1 && freeblock[i].size >= size && freeblock[i].size > max_size) {
allocaddr = freeblock[i].startaddr;
max_size = freeblock[i].size;
}
}
if (allocaddr != -1) {
for (i = 0; i < N; i++) {
if (freeblock[i].startaddr == allocaddr) {
if (freeblock[i].size > size) {
freeblock[i].startaddr += size;
freeblock[i].size -= size;
} else {
freeblock[i].state = 0;
}
break;
}
}
}
return allocaddr;
}
- 编写程序实现先进先出页面置换算法,并计算缺页次数,缺页率,置换次数和命中率。(2分)
参考程序:
#include <stdio.h>
//初始化内存队列
void initializeList(int list[],int number){
int i;
for (i = 0; i < number; i ++) {
list[i] = -1;
}
}
//展示要访问页面号数组
void showList(int list[], int number){
int i;
for (i = 0; i < number; i ++) {
printf("%2d",list[i]);
}
printf("\n");
}
//展示当前内存状态
void showMemoryList(int list[],int phyBlockNum){
int i;
for (i = 0; i < phyBlockNum; i ++) {
if (list[i] == -1) {
break;
}
printf(" |%d|",list[i]);
}
printf("\n");
}
//计算各项指标
void informationCount(int missingCount,int replaceCount,int pageNum){
printf("缺页次数:%d 缺页率:%d/%d\n",missingCount,missingCount,pageNum);
double result = (double)(pageNum - missingCount)/(double)pageNum;
printf("置换次数:%d 命中率:%.2f\n",replaceCount,result);
}
//先进先出置换算法
void replacePageByFIFO(int memoryList[],int phyNum,int strList[],int pageNum){
//置换次数
int replaceCount = 0;
//缺页次数
int missingCount = 0;
//记录当前最早进入内存的下标
int pointer = 0;
//记录当前页面的访问情况: 0 未访问
int i,j,isVisited = 0;
for (i = 0; i < pageNum; i ++) {
isVisited = 0;
//判断是否需要置换->内存已满且需要访问的页面不在内存中
for (j = 0; j < phyNum; j ++) {
if (memoryList[j] == strList[i]) {
//该页面已经存在内存中
//修改访问情况
isVisited = 1;
//展示
printf("%d\n",strList[i]);
break;
}
if (memoryList[j] == -1) {
//页面不在内存中且内存未满->直接存入
memoryList[j] = strList[i];
//修改访问情况
isVisited = 1;
missingCount ++;
//展示
printf("%d\n",strList[i]);
showMemoryList(memoryList, phyNum);
break;
}
}
if (!isVisited) {
//当前页面还未被访问过->需要进行页面置换
//直接把这个页面存到所记录的下标中
memoryList[pointer] = strList[i];
//下标指向下一个
pointer ++;
//如果到了最后一个,将下标归零
if (pointer > phyNum-1) {
pointer = 0;
}
missingCount ++;
replaceCount ++;
//展示
printf("%d\n",strList[i]);
showMemoryList(memoryList, phyNum);
}
}
informationCount(missingCount, replaceCount, pageNum);//计算各项指标
}
int main(int argc, const char * argv[]) {
//物理块的数量
int phyBlockNum;
printf("请输入物理块数量:\n");
scanf("%d",&phyBlockNum);
//生成内存队列数组
int memoryList[phyBlockNum];
//初始化内存状态
initializeList(memoryList, phyBlockNum);
//showMemoryList(memoryList,phyBlockNum);
//页面数量
int pageNum;
printf("请输入要访问的页面总数:\n");
scanf("%d",&pageNum);
//保存页面号数组
int pageNumStrList[pageNum];
int i;
//将要访问的页面号存入数组中
printf("请输入要访问的页面号:\n");
for (i = 0; i < pageNum; i ++) {
scanf("%d",&pageNumStrList[i]);
}
//显示要访问页面号数组中内容
showList(pageNumStrList, pageNum);
int chose;
while (1) {
printf("请选择所需的置换算法:\n");
printf("1.FIFO 2.退出\n");
scanf("%d",&chose);
switch (chose) {
case 1:
//显示要访问页面号数组中内容
showList(pageNumStrList, pageNum);
//调用先进先出置换算法
replacePageByFIFO(memoryList, phyBlockNum, pageNumStrList, pageNum);
//重新初始化内存
initializeList(memoryList , phyBlockNum);
break;
default:
return 0;
break;
}
}
return 0;
}
编译及执行过程以及结果截屏:

分析程序功能:
1.用户输入物理块数量和要访问的页面总数。
2.用户输入要访问的页面号,将其存入一个数组中。
3.用户选择要使用的置换算法,目前只支持FIFO算法。
4.程序调用replacePageByFIFO函数来执行FIFO算法: a. 该函数通过遍历页面号数组,判断页面是否在内存中,如果在则不进行操作,如果不在则进行页面置换。 b. 如果内存未满,直接将页面存入内存;如果内存已满,将最早进入内存的页面置换出去,并将新页面存入内存。 c. 统计缺页次数和置换次数,并展示当前内存状态。
5.调用informationCount函数计算并展示各项指标,包括缺页次数、缺页率、置换次数和命中率。
6.重新初始化内存,回到步骤3,直到用户选择退出程序为止。
- 编程实现其它页面置换算法(如最近最久未使用算法或最佳置换算法等),计算缺页次数,缺页率,置换次数和命中率。(1分)
#include <stdio.h>
#define MAX_PAGES 100
#define MAX_FRAMES 10
// 初始化页面访问序列
void initializePages(int pages[], int numPages) {
printf("请输入页面访问序列(以-1结束):\n");
int i = 0;
do {
scanf("%d", &pages[i]);
i++;
} while (pages[i-1] != -1 && i < numPages);
}
// LRU算法
int lru(int pages[], int numPages, int numFrames) {
int frames[MAX_FRAMES] = {-1}; // 帧表
int lruCount[MAX_FRAMES] = {0}; // 记录每个帧最后一次使用的时间
int numFaults = 0; // 缺页次数
int numReplacements = 0; // 置换次数
int numHits = 0; // 命中次数
int time = 0; // 记录时间
for (int i = 0; i < numPages; i++) {
int page = pages[i];
int j;
for (j = 0; j < numFrames; j++) {
if (frames[j] == page) {
numHits++;
lruCount[j] = time;
break;
}
}
if (j == numFrames) {
int lruIndex = 0;
for (int k = 1; k < numFrames; k++) {
if (lruCount[k] < lruCount[lruIndex]) {
lruIndex = k;
}
}
frames[lruIndex] = page;
lruCount[lruIndex] = time;
numFaults++;
numReplacements++;
}
time++;
}
printf("\nLRU算法结果:\n");
printf("缺页次数:%d\n", numFaults);
printf("缺页率:%f\n", (float)numFaults/numPages);
printf("置换次数:%d\n", numReplacements);
printf("命中率:%f\n\n", (float)numHits/numPages);
return numFaults;
}
// 最佳置换算法
int optimal(int pages[], int numPages, int numFrames) {
int frames[MAX_FRAMES] = {-1}; // 帧表
int numFaults = 0; // 缺页次数
int numReplacements = 0; // 置换次数
int numHits = 0; // 命中次数
for (int i = 0; i < numPages; i++) {
int page = pages[i];
int j;
for (j = 0; j < numFrames; j++) {
if (frames[j] == page) {
numHits++;
break;
}
}
if (j == numFrames) {
int replaceIndex = -1;
int found = 0;
for (int k = 0; k < numFrames; k++) {
int m;
for (m = i+1; m < numPages; m++) {
if (frames[k] == pages[m]) {
found = 1;
if (m > replaceIndex) {
replaceIndex = m;
}
break;
}
}
if (!found) {
replaceIndex = k;
break;
}
}
frames[replaceIndex] = page;
numFaults++;
numReplacements++;
}
}
printf("最佳置换算法结果:\n");
printf("缺页次数:%d\n", numFaults);
printf("缺页率:%f\n", (float)numFaults/numPages);
printf("置换次数:%d\n", numReplacements);
printf("命中率:%f\n\n", (float)numHits/numPages);
return numFaults;
}
int main() {
int pages[MAX_PAGES];
int numPages;
int numFrames;
printf("请输入页面数:");
scanf("%d", &numPages);
printf("请输入帧数:");
scanf("%d", &numFrames);
initializePages(pages, numPages);
lru(pages, numPages, numFrames);
optimal(pages, numPages, numFrames);
return 0;
}
- 编程用动态分区链形式模拟动态分区管理中内存的分配和回收,采用3种算法(首次适应算法,最佳适应算法,最坏适应算法)实现。(附加题)
#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 100
typedef struct Node {
int start;
int end;
int size;
int status; // 0表示未分配,1表示已分配
struct Node* next;
} Node;
Node* head = NULL;
// 初始化内存
void initMemory() {
head = (Node*)malloc(sizeof(Node));
head->start = 0;
head->end = MAX_SIZE;
head->size = MAX_SIZE;
head->status = 0;
head->next = NULL;
}
// 打印内存分配情况
void printMemory() {
Node* current = head;
while (current != NULL) {
printf("[%d-%d] Size: %d ", current->start, current->end, current->size);
if (current->status == 0) {
printf("Status: Free\n");
} else {
printf("Status: Allocated\n");
}
current = current->next;
}
printf("\n");
}
// 首次适应算法分配内存
void* allocateFirstFit(int size) {
Node* current = head;
while (current != NULL) {
if (current->status == 0 && current->size >= size) {
int remainingSize = current->size - size;
if (remainingSize > 0) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->start = current->start;
newNode->end = current->start + size;
newNode->size = size;
newNode->status = 1;
newNode->next = current->next;
current->start = newNode->end;
current->size = remainingSize;
current->next = newNode;
} else {
current->status = 1;
}
return (void*)current->start;
}
current = current->next;
}
return NULL;
}
// 最佳适应算法分配内存
void* allocateBestFit(int size) {
Node* current = head;
Node* bestFitBlock = NULL;
int bestFitSize = MAX_SIZE + 1;
while (current != NULL) {
if (current->status == 0 && current->size >= size && current->size < bestFitSize) {
bestFitBlock = current;
bestFitSize = current->size;
}
current = current->next;
}
if (bestFitBlock != NULL) {
int remainingSize = bestFitBlock->size - size;
if (remainingSize > 0) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->start = bestFitBlock->start;
newNode->end = bestFitBlock->start + size;
newNode->size = size;
newNode->status = 1;
newNode->next = bestFitBlock->next;
bestFitBlock->start = newNode->end;
bestFitBlock->size = remainingSize;
bestFitBlock->next = newNode;
} else {
bestFitBlock->status = 1;
}
return (void*)bestFitBlock->start;
}
return NULL;
}
// 最坏适应算法分配内存
void* allocateWorstFit(int size) {
Node* current = head;
Node* worstFitBlock = NULL;
int worstFitSize = -1;
while (current != NULL) {
if (current->status == 0 && current->size >= size && current->size > worstFitSize) {
worstFitBlock = current;
worstFitSize = current->size;
}
current = current->next;
}
if (worstFitBlock != NULL) {
int remainingSize = worstFitBlock->size - size;
if (remainingSize > 0) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->start = worstFitBlock->start;
newNode->end = worstFitBlock->start + size;
newNode->size = size;
newNode->status = 1;
newNode->next = worstFitBlock->next;
worstFitBlock->start = newNode->end;
worstFitBlock->size = remainingSize;
worstFitBlock->next = newNode;
} else {
worstFitBlock->status = 1;
}
return (void*)worstFitBlock->start;
}
return NULL;
}
// 回收内存
void deallocate(void* addr) {
Node* current = head;
Node* prev = NULL;
while (current != NULL) {
if (current->start == (int)addr) {
current->status = 0;
// 合并相邻的空闲块
if (prev != NULL && prev->status == 0) {
prev->end = current->end;
prev->size += current->size;
prev->next = current->next;
free(current);
current = prev;
}
if (current->next != NULL && current->next->status == 0) {
current->end = current->next->end;
current->size += current->next->size;
Node* temp = current->next->next;
free(current->next);
current->next = temp;
}
return;
}
prev = current;
current = current->next;
}
}
int main() {
initMemory();
printMemory();
void* addr1 = allocateFirstFit(20);
printf("Allocated block: [0-20]\n");
printMemory();
void* addr2 = allocateBestFit(30);
printf("Allocated block: [20-50]\n");
printMemory();
void* addr3 = allocateWorstFit(40);
printf("Allocated block: [50-90]\n");
printMemory();
deallocate(addr2);
printf("Deallocated block: [20-50]\n");
printMemory();
deallocate(addr1);
printf("Deallocated block: [0-20]\n");
printMemory();
void* addr4 = allocateBestFit(70);
printf("Allocated block: [0-70]\n");
printMemory();
return 0;
}
三、实验总结和体会(1分)
通过实际编写程序实现内存分配与回收的算法,我更加理解了这些算法的原理和实现方式。其次,通过对比不同算法的性能,我能够更加准确地选择适合当前需求的算法。最后,我还学到了一些实际应用中的内存管理技巧和策略,为日后的工作打下了基础。
在实验过程中,我发现自己对于某些算法和概念的理解还不够深入,需要进一步学习和加强。此外,我在实验中遇到了一些问题,如编写不完善的算法导致程序出错等,这些问题提醒我需要加强对代码质量和细节的把控。
相关文章:
实验五 内存管理实验
实验五 内存管理实验 一、实验目的 1、了解操作系统动态分区存储管理过程和方法。 2、掌握动态分区存储管理的主要数据结构--空闲表区。 3、加深理解动态分区存储管理中内存的分配和回收。 4、掌握空闲区表中空闲区3种不同放置策略的基本思想和实现过程。 5、通过模拟程…...
用Webpack 基础配置快速搭建项目开发环境
Webpack 是一个现代 JavaScript 应用程序的静态模块打包工具,但是Webpack有大量的配置项,对新手不太友好,但是我们可以根据webpack-cli的init命令根据项目需求快速生成webpack的配置文件,本文将手把手教你如何用 Webpack 和 npm 快…...
Axios 介绍及使用指南
本文将基于 Axios 原理,安装及封装方面展开描述,话不多说,现在发车! 一、原理 Axios 中文文档:起步 | Axios中文文档 | Axios中文网 赛前科普: 下文将涉及到三个关键词:Axios,Ajax…...
接口自动化测试(二)
一、接口测试流程:接口文档、用例编写 拿到接口文档——编写接口用例以及评审——进行接口测试——工具/自动化框架进行自动化用例覆盖(70%)——输出测试报告 自动化的目的一般是为了回归 第一件事情:理解需求,学会看接口文档 只需要找到我…...
Arduino+ESP826601s模块连接阿里云并实现温湿度数据上报
ArduinoESP826601s模块连接阿里云并实现温湿度数据上报 一、前言二、准备工作三、程序代码1. Arduino的程序2. ESP826601的程序3. 上面程序需要注意的地方 四、运行结果五、结束语 一、前言 看完我这三篇文章,相信各位朋友对于阿里云物联网平台的使用都有了一定的认…...
本地生活服务信息分类信息系统
最近在找分类信息系统,看了很多市面上常见的分类信息系统: 1,私集分类信息系统 2,火鸟分类信息系统 3,觅分类信息系统 4,框分类信息系统 5,蚂蚁分类信息系统 发现很多分类信息系统,…...
React Native 0.79 稳定版发布,更快的工具、更多改进
React Native 0.79 已发布。此版本在多个方面进行了性能改进,并修复了一些漏洞。首先,得益于延迟哈希技术,Metro 的启动速度变快了,并且对包导出提供了稳定支持。由于 JS 包压缩方式的改变等原因,Android 的启动时间也…...
【Dify应用】连接数据库生成Echarts图表
这里写自定义目录标题 需求文档内容测试环境实际效果工作流内容工具安装B工作流详解A工作流详解优化建议 需求 甲方要求。根据自然语言生成对应Echarts图表,并且数据来源于私有数据库。 文档内容 本文档内容主要展示使用Dify(本地源码)进行…...
无刷电机槽数相同、转子极数不同的核心区别
一、基础原理差异 无刷电机的核心参数: 槽数(定子槽数,记为 ( Z )):定子铁芯上的绕组槽数量,决定绕组布局。极数(转子磁极数,记为 ( 2p )):转子上的永磁体磁极对数(总极数为 ( 2p ),如 ( p=4 ) 表示 8 极)。核心关系:槽极配合(( Z/2p ))决定电机电磁结构,相同…...
RAG 实战|用 StarRocks + DeepSeek 构建智能问答与企业知识库
文章作者: 石强,镜舟科技解决方案架构师 赵恒,StarRocks TSC Member 👉 加入 StarRocks x AI 技术讨论社区 https://mp.weixin.qq.com/s/61WKxjHiB-pIwdItbRPnPA RAG 和向量索引简介 RAG(Retrieval-Augmented Gen…...
JavaScript 性能优化实战
一、代码执行效率优化 1. 减少全局变量的使用 全局变量在 JavaScript 中会挂载在全局对象(浏览器环境下是window,Node.js 环境下是global)上,频繁访问全局变量会增加作用域链的查找时间。 // 反例:使用全局变量 var globalVar = example; function someFunction() {con…...
ubuntu 22.04 使用ssh-keygen创建ssh互信账户
现有两台ubuntu 22.04服务器,ip分别为192.168.66.88和192.168.88.66。需要将两台服务器创建新用户并将新用户做互信。 创建账户 adduser user1 # 如果此用户不想使用密码,直接一直回车就行,创建的用户是没法使用用户密码进行登陆的 su - …...
【Linux网络】Socket 编程TCP
🌈个人主页:秦jh__https://blog.csdn.net/qinjh_?spm1010.2135.3001.5343 🔥 系列专栏:https://blog.csdn.net/qinjh_/category_12891150.html 目录 TCP socket API 详解 socket(): bind(): listen(): accept(): connect V0…...
C++指针与内存管理深度解析
前言: 在C开发的道路上,指针和内存管理就像是两个既强大又危险的朋友。掌握它们就如同学会驾驭一辆高性能跑车,稍有不慎可能导致灾难,但一旦熟练掌握,便能发挥出惊人的性能和灵活性。今天就让我们一起深入探讨C中的指…...
ESP32-idf学习(二)esp32C3作服务端与电脑蓝牙数据交互
一、当前需求 目前是想利用蓝牙来传输命令,或者一些数据,包括电脑、手机与板子的数据传输,板子与板子之间的数据传输。构思是一个板子是数据接收终端,在电脑或手机下发指令后,再给其他板子相应指令,也需要…...
NHANES指标推荐:CMI
文章题目:Association between cardiometabolic index and biological ageing among adults: a population-based study DOI:10.1186/s12889-025-22053-3 中文标题:成年人心脏代谢指数与生物衰老之间的关系:一项基于人群的研究 发…...
前端单元测试实战:如何开始?
实战:如何开始单元测试 1.安装依赖 npm install --save-dev jest2.简单的例子 首先,创建一个 sum.js 文件 ./sum.js function sum(a, b) {return a b; }module.exports sum;创建一个名为 sum.test.js 的文件,这个文件包含了实际测试内…...
react-native搭建开发环境过程记录
主要参考:官网的教程 https://reactnative.cn/docs/environment-setup 环境介绍:macos ios npm - 已装node18 - 已装,通过nvm进行版本控制Homebrew- 已装yarn - 已装ruby - macos系统自带的2.2版本。watchman - 正常安装Xcode - 正常安装和…...
【数据库系统概论】第3章 SQL(四)视图(超详细)
视图(View)是数据库中的虚拟表 通过执行查询定义并存储在数据库中,可以像普通表一样被查询和使用。 视图本身并不存储数据,而是基于一个或多个表的查询结果动态生成。 视图的概念 视图( View )是由其它表或视图上的查询所定义…...
观察者模式详解与C++实现
1. 模式定义 观察者模式(Observer Pattern)是一种行为型设计模式,定义了对象间的一对多依赖关系。当一个对象(被观察者/主题)状态改变时,所有依赖它的对象(观察者)都会自动收到通知…...
空调制冷量和功率有什么关系?
空调的制冷量和功率是衡量空调性能的两个核心参数,二者既有区别又紧密相关,以下是具体解析: 1. 基本定义 制冷量(Cooling Capacity)指空调在单位时间内从室内环境中移除的热量,单位为 瓦特(W) 或 千卡/小时(kcal/h)。它直接反映空调的制冷能力,数值越大,制冷效果越…...
【python报错解决训练】
在编程开发中,正确解读报错信息是解决问题的关键技能。以下是系统学习解读报错信息的方法指南: 一、理解报错信息的核心结构 典型的报错信息包含以下要素(以Python为例): Traceback (most recent call last):File &q…...
UE5 关卡序列
文章目录 介绍创建一个关卡序列编辑动画添加一个物体编辑动画时间轴显示秒而不是帧时间轴跳转到一个确定的时间时间轴的显示范围更改关键帧的动画插值方式操作多个关键帧 播放动画 介绍 类似于Unity的Animation动画,可以用来录制场景中物体的动画 创建一个关卡序列…...
AI测试用例生成平台
AI测试用例生成平台 项目背景技术栈业务描述项目展示项目重难点 项目背景 针对传统接口测试用例设计高度依赖人工经验、重复工作量大、覆盖场景有限等行业痛点,基于大语言模型技术实现接口测试用例智能生成系统。 技术栈 LangChain框架GLM-4模型Prompt Engineeri…...
C#中扩展方法和钩子机制使用
1.扩展方法: 扩展方法允许向现有类型 “添加” 方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。扩展方法是一种特殊的静态方法,但可以像实例方法一样进行调用。 使用场景: 1.当无法修改某个类的源代码&#…...
大语言模型减少幻觉的常见方案
什么是大语言模型的幻觉 大语言模型的幻觉(Hallucination)是指模型在生成文本时,输出与输入无关、不符合事实、逻辑错误或完全虚构的内容。这种现象主要源于模型基于概率生成文本的本质,其目标是生成语法合理、上下文连贯的文本&…...
YOLOv5、YOLOv6、YOLOv7、YOLOv8、YOLOv9、YOLOv10、YOLOv11、YOLOv12的网络结构图
文章目录 一、YOLOv5二、YOLOv6三、YOLOv7四、YOLOv8五、YOLOv9六、YOLOv10七、YOLOv11八、YOLOv12九、目标检测系列文章 本文将给出YOLO各版本(YOLOv5、YOLOv6、YOLOv7、YOLOv8、YOLOv9、YOLOv10、YOLOv11、YOLOv12)网络结构图的绘制方法及图。本文所展…...
03 UV
04 Display工具栏_哔哩哔哩_bilibili 讲的很棒 ctrlMMB 移动点 s 打针 ss 批量打针...
AIGC-几款本地生活服务智能体完整指令直接用(DeepSeek,豆包,千问,Kimi,GPT)
Unity3D特效百例案例项目实战源码Android-Unity实战问题汇总游戏脚本-辅助自动化Android控件全解手册再战Android系列Scratch编程案例软考全系列Unity3D学习专栏蓝桥系列AIGC(GPT、DeepSeek、豆包、千问、Kimi)👉关于作者 专注于Android/Unity和各种游戏开发技巧,以及各种资…...
Django ORM 定义模型
提示:定义模型字段的类型 文章目录 一、字段类型二、字段属性三、元信息 一、字段类型 常用字段 字段名描述备注AutoFieldint 自增必填参数 primary_keyTrue,无该字段时,django自动创建一个 BigAutoField,一个model不能有两个Au…...
