06.C语言学习4.0

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的连续空间。

malloccalloc的区别:

(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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值