内存管理函数


动态内存管理

原来我们程序中要使用的变量都要提前定义好,不能在数组运行中才开始定义,后来C99出现了变长数组,能提高代码的灵活度,比如询问用户需要定义几个整型数据,得到答案后再定义所需要长度的整型数据。但局限性是该数组创建出来后便不能再修改。而动态内存管理就是在提高代码灵活度的同时又能避免这个不可变的局限性。
在这里插入图片描述

molloc

void *molloc(size_t size);//函数原型
molloc函数向系统申请分配size个字节的内存空间,并返回一个指向这块空间的指针.
由于返回类型是void指针(void*),所以它可以被转换为任何类型的数据,如果函数调用失败,返回值是NULL。
例子 使用malloc申请一块int大小的空间,并进行打印,其中malloc申请的空间位于堆上。要记得释放申请的内存空间

#include<stdio.h>
#include<stdlib.h>//分配的内存必须的头文件
int main(){
int *ptr;
pre=(int*)malloc(sizeof(int));//显示转换为int* 指针
if(ptr==NULL)
{
printf("分配失败!\n");
exit(1);
}
scanf("%d",ptr);
printf("你输入的整数是:%d\n",*ptr);
return 0;
}
free

在这里插入图片描述
不能用free释放普通局部变量
例子:

#include<stdio.h>
#include<stdlib.h>//分配的内存必须的头文件
int main(){
int *ptr;
pre=(int*)malloc(sizeof(int));//显示转换为int* 指针
if(ptr==NULL)
{
printf("分配失败!\n");
exit(1);
}
scanf("%d",ptr);
printf("你输入的整数是:%d\n",*ptr);
free(ptr);
printf("你输入的整数是:%d\n",*ptr);//这里输出将不再是之前的输入数字,因为已经被释放,指向的是一个垃圾地址
return 0;
}
内存泄漏
  1. 申请的动态内存没有及时释放
while(1)
{
malloc(1024);//不断申请内存空间,死循环
}

C语言不具备垃圾回收机制,需要我们自己动手,用free进行释放
2. 申请的动态内存指针指向了别的地址,但没有释放原来所指向的空间,并尝试对一个变量进行释放

int *ptr;
int num=123;
pre=(int*)malloc(sizeof(int));//显示转换为int* 指针
if(ptr==NULL)
{
printf("分配失败!\n");
exit(1);
}
scanf("%d",ptr);
printf("你输入的整数是:%d\n",*ptr);
ptr=&num;//这里会出现问题,丢失了内存块的地址
printf("...");
free(ptr);//这里尝试对一个局部变量进行了释放
return 0;

在这里插入图片描述

malloc申请一块任意尺寸的内存空间
#include<stdio.h>
#include<stdlib.h>

int main()
{
int *ptr=NULL;
int num,i;
printf("请输入待录入整数的个数:");
scanf("%d",&num);
ptr=(int *)malloc(num*sizeof(int));
for(i=0;i<num;i++)
{
scanf("%d",&ptr[i]);//这里要注意一定不要忘了&符号
}
printf("你录入的整数是:");
for(int j=0;j<num;j++)
{
printf("%d",ptr[j]);
}
putchar('\n');
free(ptr);//释放内存
return 0;
}

scanf("%d",&ptr[i]);
说明:
首先数组和指针是等价的,区别就在于跨度
之前ptr是按照数组的跨度声明的,所以这里直接当做数组处理,取址就是因为scanf,如果ptr是按照整型指针声明,那么久直接ptr+i了

初始化内存空间

在这里插入图片描述
mem开头的函数的返回值是void*

memset(ptr,0,N*sizeof(int));//ptr是指向内存块的指针,0是初始化的值,N*sizeof(int)是要初始化的内存空间有多大

用calloc替代malloc,能直接初始化,不需要memset
在这里插入图片描述
在这里插入图片描述

动态数据扩展

举例原来10个数组的空间不够用了
在这里插入图片描述
可以用realloc替代memcpy的重分内存空间的形式
在这里插入图片描述
在这里插入图片描述
例子:让用户不断地输入,并更新动态内存空间,保存用户输入的数字

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

int main()
{
    
    int num;
    int count=0;
    int *ptr=NULL;//注意这里必须初始化为NULL,realloc中ptr为NULL,相当于malloc
    do{
        printf("请输入一个整数(输入-1为结束):");
        scanf("%d",&num);
        count++;
        ptr=(int*)realloc(ptr,count*sizeof(int));
        if (ptr==NULL)
        {
            exit(1);
        }
        ptr[count-1]=num;
    }while(num!=1);
    putchar('\n');
    free(ptr);
    return 0;
    
}

实战

编写一个小程序,要求使用本节课学到的函数在堆中申请一个矩阵(二维数组),矩阵的尺寸由用户指定,并且允许修改

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

#define MAX_LIMIT_MATRIX 100

void welcome(void);
int get_ins(void);
int *create_matrix(void);
void init_matrix(int *ptr);
void print_matrix(int *ptr);
void write_matrix(int *ptr);
void read_matrix(int *ptr);

void welcome(void)
{
        printf("\n============================\n");
        printf("* 欢迎使用该程序,指令如下 *\n");
        printf("* 1.生成一个 M*N 的矩阵    *\n");
        printf("* 2.初始化矩阵             *\n");
        printf("* 3.给矩阵中某个元素赋值   *\n");
        printf("* 4.读取矩阵中某个元素     *\n");
        printf("* 5.打印整个矩阵           *\n");
        printf("* 6.结束程序               *\n");
        printf("============================\n");
}

int get_ins(void)
{
        int ins;

        printf("\n请输入指令:");
        scanf("%d", &ins);

        while (ins < 1 || ins > 6)
        {
                printf("\n指令错误,请重新输入:");
                scanf("%d", &ins);
        }

        return ins;
}

int *create_matrix(void)
{
        int m, n;
        static int created = 0; // 用于判断是否已经创建过矩阵
        static int *ptr = NULL;

        if (created)
        {
                printf("矩阵已存在,是否需要重新创建?(Y/N)-> ");
                getchar(); // 清除缓冲区残留的换行符
                while (getchar() == 'N')
                {
                        return ptr;
                }
        }

        printf("请输入新矩阵的规模(M*N)-> ");
        scanf("%d*%d", &m, &n);

        while (m < 1 || n < 1)
        {
                printf("规模太小,请重新输入:");
                scanf("%d*%d", &m, &n);
        }

        while (m > MAX_LIMIT_MATRIX || n > MAX_LIMIT_MATRIX)
        {
                printf("规模太大,请重新输入:");
                scanf("%d*%d", &m, &n);
        }

        // 虽然说是矩阵是二维数组,但在C语言中它的存放形式是“平铺”的
        // 这里用realloc,支持重新创建二维数组
        // 这里多申请了两个整形空间,用于存放矩阵的长和宽
        ptr = (int *)realloc(ptr, (m * n + 2)* sizeof(int));
        if (ptr == NULL)
        {
                printf("内存申请失败!\n");
                exit(1);
        }

        printf("%d*%d 的矩阵创建成功!\n", m, n);
        created = 1;

        // 将长和宽放在前两个元素中
        ptr[0] = m;
        ptr[1] = n;

        return ptr;
}

void init_matrix(int *ptr)
{
        int m = ptr[0];
        int n = ptr[1];
        int *matrix = ptr + 2;
        int num, i, j;

        if (ptr == NULL)
        {
                printf("未检测到矩阵,请先生成矩阵!\n");
                return ;
        }

        printf("请输入一个数字:");
        scanf("%d", &num);

        for (i = 0; i < m; i++)
        {
                for (j = 0; j < n; j++)
                {
                        matrix[i * n + j] = num;
                }
        }
}

void print_matrix(int *ptr)
{
        int m = ptr[0];
        int n = ptr[1];
        int *matrix = ptr + 2;
        int i, j;

        if (ptr == NULL)
        {
                printf("未检测到矩阵,请先生成矩阵!\n");
                return ;
        }

        for (i = 0; i < m; i++)
        {
                for (j = 0; j < n; j++)
                {
                        printf("%d  ", matrix[i * n + j]);
                }
                putchar('\n');
        }
}

void write_matrix(int *ptr)
{
        int m = ptr[0];
        int n = ptr[1];
        int *matrix = ptr + 2;
        int num, x, y;

        if (ptr == NULL)
        {
                printf("未检测到矩阵,请先生成矩阵!\n");
                return ;
        }

        printf("请输入要修改的位置(行,列)-> ");
        scanf("%d,%d", &x, &y);

        if (x > m || y > n || x < 1 || y < 1)
        {
                printf("坐标输入有误!\n");
                return ;
        }

        printf("请输入一个数字:");
        scanf("%d", &num);

        matrix[(x - 1) * n + (y - 1)] = num;
}

void read_matrix(int *ptr)
{
        int m = ptr[0];
        int n = ptr[1];
        int *matrix = ptr + 2;
        int num, x, y;

        if (ptr == NULL)
        {
                printf("未检测到矩阵,请先生成矩阵!\n");
                return ;
        }

        printf("请输入要读取的位置(行,列)-> ");
        scanf("%d,%d", &x, &y);

        if (x > m || y > n || x < 1 || y < 1)
        {
                printf("坐标输入有误!\n");
                return ;
        }

        printf("第%d行,第%d列的数字是:%d\n", x, y, matrix[(x - 1) * n + (y - 1)]);
}

int main(void)
{
        int ins;
        int *ptr = NULL;

        welcome();

        while((ins = get_ins()) != 6)
        {
                switch(ins)
                {
                        case 1: ptr = create_matrix(); break;
                        case 2: init_matrix(ptr); break;
                        case 3: write_matrix(ptr); break;
                        case 4: read_matrix(ptr); break;
                        case 5: print_matrix(ptr); break;
                }
        }

        printf("\n感谢使用本程序^_^\n\n");

        free(ptr);

        return 0;
}

putchar()getchar()重申
getchar()读取标准输入的下一个字符,直到遇到文件结束标志或发生错误哦。

putchar(c)将c对应值输出到标准输出。成功的话返回c失败返回EOF
getchar()
从输入流中读取一个单个的字符,

如果输入的是字符串,函数也只读取头一个字符,如果下面还有getchar函数则接着上个getchar函数读到的下一个字符读而不需要继续输入就会返回一个读取的字符,

这里涉及到缓冲,就是我们输入的字符流其实是存在于缓冲区中,所以下一个getchar函数才能接着读取;
getchar有一个int型的返回值.当程序调用getchar时.程序就等着用户按键.用户输入的字符被存放在键盘缓冲区中.直到用户按回车为止(回车字符也放在缓冲区中).当用户键入回车之后,getchar才开始从stdio流中每次读入一个字符.getchar函数的返回值是用户输入的第一个字符的ASCII码,如出错返回-1,且将用户输入的字符回显到屏幕.
如用户在按回车之前输入了不止一个字符,其他字符会保留在键盘缓存区中,等待后续getchar调用读取.也就是说,后续的getchar调用不会等待用户按键,而直接读取缓冲区中的字符,直到缓冲区中的字符读完为后,才等待用户按键.
注意:当用getchar进行输入时,如果输入的第一个字符为有效字符(即输入是文件结束符EOF,Windows下为组合键Ctrl+Z, Unix/Linux下为组合键Ctrl+D),那么只有当最后一个输入字符为换行符’\n’(也可以是文件结束符EOF, getchar才会停止执行,整个程序将会往下执行。譬如下面程序段:

while((c = getchar()) != EOF) 
{ 
putchar(c); 
} 

执行程序,输入:abc,然后回车。则程序就会去执行puchar©,然后输出abc,这个地方不要忘了,系统输出的还有一个回车。然后可以继续输入,再次遇到换行符的时候,程序又会把那一行的输入的字符输出在终端上。
对于getchar,肯定很多初学的朋友会问,getchar不是以字符为单位读取的吗?那么,既然我输入了第一个字符a,肯定满足while循环(c = getchar()) != EOF的条件阿,那么应该执行putchar©在终端输出一个字符a。不错,我在用getchar的时候也是一直这么想的,但是程序就偏偏不着样执行,而是必需读到一个换行符或者文件结束符EOF才进行一次输出。
对这个问题的一个解释是,在大师编写C的时候,当时并没有所谓终端输入的概念,所有的输入实际上都是按照文件进行读取的,文件中一般都是以行为单位的。因此,只有遇到换行符,那么程序会认为输入结束,然后采取执行程序的其他部分。同时,输入是按照文件的方式存取的,那么要结束一个文件的输入就需用到EOF (Enf Of File). 这也就是为什么getchar结束输入退出时要用EOF的原因。
putchar
说下用法和一些细节点;

putchar是输出一个字符,

char ch=’d’;

putchar(ch)//输出’d’

putchar(‘\n’);//输出一个换行

putchar(‘a’);//输出一个字符’a’

putchar(‘\101’);//输出字符’A’

putchar(‘\015’);//输出回车不换行,只是把光标移动到本行开头,注意与换行的区别

//下面用一个题目来看其实如何获取字符和输出字符的
#include "stdio.h"
int main()
{
    char c,d,e,f;
    printf("please input two characters:\n");
    c=getchar();
    printf("第一getchar:");
    putchar(c);
    putchar('\n');
    d=getchar();
    printf("第二getchar:");
    putchar(d);
    putchar('\n');
    e=getchar();
    printf("第三getchar:");
    putchar(e);
    putchar('\n');
    f=getchar();
    printf("第四getchar:");
    putchar(f);
    putchar('\n');
    printf("---------\n");
    printf("c= %c\n",c);
    printf("d= %c\n",d);
    printf("e= %c\n",e);
    printf("f= %c\n",f);

    return 0;
}
//运行结果:
[root@localhost 1116]# ./getchar 
please input two characters:
12
第一getchar:1
第二getchar:2
第三getchar:

3
第四getchar:3
---------
c= 1
d= 2
e= 

f= 3
//分析:
getchar函数每次从缓冲区中得到一个字符,putchar函数每次输出一个字符。

首先输入了两个字符12,然后回车,注意这时写入缓存中的有3个字符12,回车。

程序中有四个getchar(),于是c='1',d='2',e='\n'。

这时运行到f=getchar();输入缓存中的三个字符均被前三个getchar获取,这时需要用户输入,

这里输入了3

于是f='3'3后面的回车没有被利用。

这便是整个流程。

printf()可以输出一个字符串,putchar()只能输出一个字符,不能输出其它的类型数据。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值