6、C语言4.0
1.堆上空间的利用
1、
stdlib
头文件中相关函数:
- malloc函数:
void* malloc(size_t size)
作用:提供多少个字节的内存
void *
:万能指针;能够转换成任何指针,能够接收任何指针,接收之后对内存的访问安全不能负责,要将void指针赋值给其他类型的指针,必须进行强制类型转换。eg:void *p1;int *p2;p2=(int*)p1;
强制类型转换:将一种数据类型转换成另外一种数据类型;格式:在变量面前+(要转的类型);可以使用强制类型转换改变 malloc
返回值的类型。eg:(int *)malloc(sizeof(int));
可以将malloc
返回值转换成int*
型
#include<stdio.h>
#include<stdlib.h>
int main()
{
int *p=(int *)malloc(sizeof(int));//申请堆上内存
if(p==NULL)//判断申请空间是否成功
{
printf("malloc error!\n");
return 0;
}
*p=10;
printf("%d\n",*p);
free(p);//释放堆上申请的空间
p=NULL;
if(p!=NULL)
{
free(p);
}
return 0;
}
注:1、堆的碎片化可能会导致我们申请不到想要的空间,所以 malloc
可能申请失败,失败返回值为NULL
,所以要对返回的指针要进行判空处理;
2、malloc申请的空间必须要手动释放,不释放就会造成内存泄漏(原理:我们申请了一个指针指向了这块内存,但是申请的指针存在栈内,在函数结束时被释放了,导致没有指针指向那块空间,就没有办法找到该内存),当申请的空间被释放的时候,要将指向这个空间的指针置空。
- free 函数:
void free(void *ptr)
作用:释放malloc申请的空间
释放的时候可能会出现二次释放的可能,所以要设置判断条件;规定一旦释放内存以后,指向那块内存的指针置为NULL,所以判断该指针是否为空来判断该内存是否已经被释放。
相关概念补充:对于服务器,如果不断地有客户端连接,并且客户端不断发送消息,对于每个连接的每个消息,服务器都需要malloc/free内存。如果服务器需要7*24运行,时间久了就会产生很多内存碎片,没有整块,最后就会malloc失败,所以就使用内存池解决;
内存池---->应用程序直接对使用的内存进行管理,避免频繁地从堆上申请释放空间,可以做到一次申请,对此分配。主要针对小块。内存池管理的是堆内存。进程开始运行的时候划分一块内存
2、malloc函数的使用
【1】、申请一维数组的空间
int *p=(int *)malloc(sizeof(int)*10);
if(p==NULL)
{
return 0;
}
for(int i=0;i<10;i++)
{
*(p+i)=i+1;
printf("%d",*(p+i));
}
printf("\n");
free(p);
p=NULL;
【2】、申请二维数组的空间:
方法1:先申请一个指针数组,然后每一个指针元素再申请一维数组的大小
void Malloc()
{
int **p=(int **)malloc(sizeof(int*)*2);//申请指针数组
for(int i=0;i<2;i++)
{
p[i]=(int*)malloc(sizeof(int)*3);//每个指针元素指向一个一位数组
}
for(int i=0;i<2;i++)
{
for(int j=0;j<3;j++)
{
p[i][j]=i*3+j+1;
printf("%d ",p[i][j]);//给二维数组的每一个元素赋值
}
printf("\n");
}
for(int i=0;i<2;i++)
{
free(p[i]);//释放指针元素指向的内存
}
free(p);//释放p指向的内存
p=NULL;
}
p | —> | p[0](8个字节) | -----> | 1,2,3(12个字节) |
---|---|---|---|---|
p[1](8个字节) | -----> | 4,5,6(12个字节) |
方法二:先申请一个数组指针,因为数组指针本身给内存划界,当它满足自身数组容量时,会自动换行,所以直接在申请指针数组时赋予二维的总长度
void Malloc2()
{
int (*p)[3]=(int (*)[3])malloc(sizeof(int)*2*3);
for(int i=0;i<2;i++)
{
for(int j=0;j<3;j++)
{
p[i][j]=i*3+j+1;
printf("%d ",p[i][j]);
}
printf("\n");
}
free(p);
}
3、
stdlib
其他函数
calloc
函数void* calloc(unsigned int num,unsigned int size);
两个参数:(1)申请的单位空间的数量
(2)单位空间的大小
作用:在内存的动态存储区中分配num
个长度为size的连续空间。
malloc
和calloc
的区别:
(1)、malloc申请空间不会对空间进行初始化,分配到空间中的数据是随机数据;calloc
会对空间进行初始化
(2)、malloc返回参数只有一个,申请更加灵活;calloc
返回参数有两个
realloc
函数void *realloc(void *ptr,size_t size)
作用:动态分配内存,可以重新分配已经分配的内存块的大小
realloc
若申请成功:函数执行完成。若没有申请成功:则realloc
函数会调用malloc函数另外开辟一个数组空间。若开辟新的数组空间成功,将原数组中的数据拷贝到新的数组中,释放掉原数组,并返回一个数组首地址,需要用一个指针来接;若开辟新的数组失败,会返回一个空地址,表示开辟新数组失败,原来的数组空间没动。
注:1、realloc
扩容之后,原指针和新指针都可以操作(申请、释放)新申请的内存
2、realloc
当第一个参数传入的是NULL,函数本身相当于malloc
3、realloc
扩容内存不够,会申请新的内存,拷贝原内存,但是原内存指针不再使用
- 课堂测试一:申请动态数组
头文件需要的东西:
1、结构体定义
2、函数的声明
头文件尽量不要放的东西:
1、别的头文件
2、常数宏
//动态数组头文件DynamicArray.c
#ifndef __DYNAMICARRAY_H_
#define __DYNAMICARRAY_H_
#define ElementType int
typedef struct DynamicArray DMArray;
struct DynamicArray{
ElementType *dp;//设置动态数组指针,申请一维数组
int size;//堆上申请单位空间大小个数
int len;//实际的元素个数
}
int ArrayInit(DMArray *array);
void FreeArray(DMArray *array);
int InsertArray(DMArray *array,ElementType element);
//DynamicArray.c文件
#include<stdio.h>
#include"DynamicArray.h"
#define true 1
#define false 0
int ArrayInit(DMArray *array)//数组的初始化
{
//申请堆上的空间 大小为size*数据类型的大小
array->dp = (ElementType *)malloc(sizeof(ElementType)*array->size);
if(array->dp==NULL)//判断申请空间是否成功
{
printf("Initnalize error!\n");//申请失败之后打印错误信息,返回false
return false;
}
return 0;
}
void FreeArray(DMArray *array)//释放数组
{
if(array->dp!=NULL)//判断指针不为空,因为释放空指针会出错
{
free(array->dp);//释放指针
array->dp=NULL;//放空之后指针要置为空
}
}
int ReallocArray(DMArray *array)//扩容动态数组
{
ElementType *temp=array->dp;//保存原先内存的备份指针
//给dp指针申请一块新的内存,大小为:数据类型 * 原先的空间个数 *2
array->dp=(ElementType *)malloc(sizeof(ElementType)*array->size*2);
if(array->dp==NULL)//判断新申请空间是否成功
{
printf("ReallocArray error!\n");//申请失败之后打印错误信息,返回false
return false;
}
for(int i=0;i<array->len;i++)//遍历原先的数组内存,将每一个元素都拷贝到新内容中
{
array->dp[i]=temp[i];
}
array->size*=2;//空间扩展到原来的二倍
free(temp);//释放原先的内存空间
return true;
}
int InsertArray(DMArray *array,ElementType element)//插入数组元素
{
if(array->len==array->size)//判断数组是否已满
{
if(ReallocArray==false)//判断扩容是否失败
{
printf("Can't cantain more elements!\n");//扩容失败打印错误信息
return false;//返回false
}
}
array->dp[element->len]=element;//扩容成功或数组没满,在数组的第len个位置赋上元素的值
array->len++;//计算数组长度的计数器加1
return 0;
}
//主函数main.c
#include<stdio.h>
#include"DynamicArray.h"
void travel(DMArray *array)
{
printf("array len :%d array size :%d \n",array->len,array->size);
for(int i=0;i<array->len;i++)
{
printf("%d ",aray->dp[i]);
}
printf("\n");
}
int main()
{
DMArray array={NULL,10,0};//初始化一个数组
ArrayInit(&array);
for(int i=0;i<10;i++)
{
InsertArray(&array,i+1);//给数组元素赋值
}
travel(&array);//遍历输出每一个数组的值
for(int i=0;i<10;i++)
{
InsertArray(&array,i+1);//再插入10个元素
}
travel(&array);//再次遍历输出
FreeArray(&array);//释放数组内存
return 0;
}
- 课堂测试2:魔塔游戏
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<string.h>
#include"MyString.h"
#include"DynamicArray.h"
#define Map_Size 5
#define true 1
#define false 0
struct Player//创建玩家结构体特性
{
int HP;//血量
int attack;//攻击力
int x;//当前横坐标坐标
int y;//当前纵坐标
};
struct Monster//定义怪兽结构体特性
{
MyString name;//怪兽名字
int HP;//血量
int attack;//攻击力
int x;//当前横坐标
int y; //当前纵坐标
};
//定义药物结构体特性
struct Treasure
{
MyString name;//药物名称
int value;//药物价值
int x;//当前横坐标
int y;//当前纵坐标
};
//初始化玩家属性
void InitPlayer(struct Player* player)
{
player->HP = 150;
player->attack = 20;
player->x = 0;
player->y = 0;
}
//随机数函数
int GetRandNumber(int max)
{
return rand()%max;// rand()%100 1~99
}
//创建怪物对象,返回的是一个指针
struct Monster* CreateMonster(const char *name,int HP,int attack)
{
//动态申请结构体大小的内存空间用来存放怪兽
struct Monster *monster=(struct Monster*)malloc(sizeof(struct Monster));
if(monster==NULL)//申请失败
{
printf("Create Monster Error!\n");//创建失败,打印输出错误信息,并返回空指针;
return NULL;
}
monster->HP = HP;//血量
monster->attack = attack;//攻击力
Initialize(&monster->name,name);//姓名
monster->x = GetRandNumber(Map_Size);//怪兽坐标随机
monster->y = GetRandNumber(Map_Size);
return monster;//创建成功返回怪兽指针
}
//创建宝物对象,返回一个指针
struct Treasure* CreateTreasure(const char *name,int value)
{
//申请动态结构体空间大小,用来存放宝物
struct Treasure *treasure=(struct Treasure*)malloc(sizeof(struct Treasure));
if(treasure==NULL)//判断申请是否成功
{
printf("Create Treasure Error!\n");//创建失败,打印错误信息,返回空指针;
return NULL;
}
Initialize(&treasure->name,name);//初始化宝物名字
treasure->value = value;//价值
treasure->x = GetRandNumber(Map_Size);//宝物的坐标
treasure->y = GetRandNumber(Map_Size);
return treasure;
}
void InitMonsters(struct DynamicArray *array)//初始化一个动态指针用来接创建的怪物指针
{
if(ArrayInit(array)==false)//初始化指针失败
{
return ;
}
//insert monster
InsertArray(array,CreateMonster("巨龙",100,20));//插入元素
InsertArray(array,CreateMonster("食人魔",50,10));
InsertArray(array,CreateMonster("食人魔",50,10));
InsertArray(array,CreateMonster("食人魔",50,10));
InsertArray(array,CreateMonster("哥布林",20,5));
InsertArray(array,CreateMonster("哥布林",20,5));
InsertArray(array,CreateMonster("哥布林",20,5));
InsertArray(array,CreateMonster("哥布林",20,5));
InsertArray(array,CreateMonster("哥布林",20,5));
}
//初始化一个动态指针用来接创建的宝藏指针
void InitTreasure(struct DynamicArray *array)
{
if(ArrayInit(array)==false)
{
return ;
}
//insert monster
InsertArray(array,CreateTreasure("宝藏",100));
InsertArray(array,CreateTreasure("珍珠",50));
InsertArray(array,CreateTreasure("珍珠",50));
InsertArray(array,CreateTreasure("珍珠",50));
InsertArray(array,CreateTreasure("药品",10));
InsertArray(array,CreateTreasure("药品",10));
InsertArray(array,CreateTreasure("药品",10));
InsertArray(array,CreateTreasure("药品",10));
InsertArray(array,CreateTreasure("药品",10));
}
//移动函数
void MakeMove(struct Player *player,char symbol)
{
switch(symbol)//判断输入的是什么功能
{
case 'w':player->y--;break;//初始化坐标高处是低坐标,向上走y--
case 's':player->y++;break;//向下走y++
case 'a':player->x--;break;//向左移动x--
case 'd':player->x++;break;//向右移动x++
default:break;
}
if(player->x<0) player->x=0;//当玩家移动到左边界将坐标设为0
if(player->x >= Map_Size) player->x=Map_Size-1;//当玩家移动到右边界将右坐标设为边界最大值
if(player->y<0) player->y=0;//向上移动到最上边,将纵坐标设为0
if(player->y >= Map_Size) player->y=Map_Size-1;//上下移动到最下边,将坐标设为边界最大值
}
//设置战斗函数,用来模拟玩家和怪物战斗过程
int Battle(struct Player *player,struct Monster* monster)
{
printf("您遭遇了 %s 血量:%d 攻击力:%d\n",monster->name.string,monster->HP,monster->attack);
printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
printf("| 开始战斗 |\n");
printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
while(player->HP>0&&monster->HP>0)//当玩家和怪物的血量都>0时开始战斗
{
printf("请选择您要执行的行为:\n ");
printf("1、普通攻击\n ");
printf("2、暴击\n ");
printf("3、逃跑\n ");
int choice;//输入要执行的操作
scanf("%d",&choice);
printf("======================战斗报告====================\n");
switch(choice)//进行判断要执行的操作
{
case 1: //普通攻击,根据攻击力玩家和怪物的血量都进行相应的减值
player->HP -= monster->attack;
monster->HP -= player->attack;
printf("%s 对你造成了 %d 伤害\n",monster->name.string,monster->attack);
printf("你对 %s 造成了 %d 伤害\n",monster->name.string,player->attack);
break;
case 2://暴击,有50%的可能失败,成功则攻击力翻倍,失败则只能被怪物打
if(GetRandNumber(2)==1)//暴击成功
{
printf("%s 对你造成了 %d 伤害\n",monster->name.string,monster->attack);
printf("你对 %s 造成了 %d 伤害\n",monster->name.string,player->attack*2);
player->HP -=monster->attack;
monster->HP -=player->attack*2;
break;
}
else//暴击失败
{
printf("%s 对你造成了 %d 伤害\n",monster->name.string,monster->attack);
printf("暴击失败无法造成伤害\n");
player->HP -=monster->attack;
break;
}
case 3://逃跑,依然只有50%的可能性,逃跑成功则随机传送到安全的位置
if(GetRandNumber(2)==1)
{
printf("逃跑成功\n");
player->x = rand()%5;
player->y = rand()%5;
printf("已使用魔法阵进行传送,现在位置为<%d,%d>\n",player->x,player->y);
return true;
}
else//逃跑失败,怪物对你造成伤害
{
printf("逃跑失败\n");
printf("%s 对你造成了 %d 伤害\n",monster->name.string,monster->attack);
player->HP -=monster->attack;
break;
}
default:
break;
}
printf("======================战斗结束====================\n");
if(player->HP<=0)//当玩家的血量<=0时,游戏结束
{
printf("你被 %s 击败了",monster->name.string);
player->HP=0;
return false;
}
if(monster->HP<=0)//怪物击毙,游戏继续
{
monster->HP=0;
printf("你击败了 %s ",monster->name.string);
return true;
}
}
}
void InitBoard(char (*p)[Map_Size])//初始化地图
{
for(int i=0;i<Map_Size;i++)
{
for(int j=0;j<Map_Size;j++)
{
p[i][j] = '-';
}
}
}
void PrintMap(char (*p)[Map_Size],struct Player *player)
{
InitBoard(p);
p[player->y][player->x] = 'X';
printf(" ");
for(int i=0;i<Map_Size;i++)
{
printf(" %2d ",i+1);
}
printf("\n");
for(int i=0;i<Map_Size;i++)
{
printf("%2d ",i+1);
for(int j=0;j<Map_Size;j++)
{
printf(" %c ",p[i][j]);
}
printf("\n");
}
}
int main()
{
srand(time(NULL));
system("clear");
printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
printf("| 欢迎来到魔塔世界! |\n");
printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
char board[Map_Size][Map_Size];
InitBoard(board);
struct Player player;
InitPlayer(&player);
struct DynamicArray monsters={NULL,10,0};
InitMonsters(&monsters);
struct DynamicArray treasures={NULL,10,0};
InitMonsters(&treasures);
while(1)
{
PrintMap(board,&player);
printf("你当前所在位置是<%d,%d>\n",player.x+1,player.y+1);
for(int i=0;i<monsters.len;i++)
{
struct Monster* monster = (struct Monster *)monsters.dp[i];
if(monster->HP >0 && monster->x==player.x && monster->y==player.y)
{
if(Battle(&player,monster)==false)
{
printf("Game over!\n");
break;
}
}
}
if(player.HP==0)
{
break;
}
printf("请选择你要进行的移动(输入'w','a','s','d')");
printf("\n");
char choice;
scanf("%c",&choice);
MakeMove(&player,(char)choice);
}
for(int i=0;i<monsters.len;i++)
{
struct Monster *monster=(struct Monster *)monsters.dp[i];
free(monster->name.string);
free(monster);
}
free(monsters.dp);
return 0;
}