首页 > 系统相关 >C语言:分配内存malloc()和free()

C语言:分配内存malloc()和free()

时间:2022-09-01 21:47:16浏览次数:49  
标签:malloc 函数 ptd free 分配内存 内存 数组

malloc()函数

C可以在程序运行时分配更多的内存,主要通过malloc()函数,该函数接受一个参数:所需的内存字节数。

malloc()函数会找到合适的空闲内存块,这样的内存是匿名的。也就是说,malloc()分配内存,但是不会为其赋名。然而,它确实返回动态分配内存块的首字节地址。因此,可以把该地址赋给一个指针变量,并使用指针访问这块内存。因为char表示1字节,malloc()的返回类型通常被定义为指向char的指针。

然而,从ANSI C标准开始,C使用一个新的类型:指向void的指针。该类型相当于一个“通用指针”。

malloc()函数可用于返回指向数组的指针、指向结构的指针等,所以通常该函数的返回值会被强制转换为匹配的类型。

如果malloc()分配内存失败,将返回空指针。

我们试着用malloc()创建一个数组。除了用malloc()在程序运行时请求一块内存,还需要一个指针记录这块内存的位置。例如,考虑下面的代码:

double * ptd;
ptd = (double *) malloc(30 * sizeof(double));

注意,指针ptd被声明为指向一个double类型,而不是指向内含30个double类型值的块。回忆一下,数组名是该数组首元素的地址。因此,如果让ptd指向这个块的首元素,便可像使用数组名一样使用它。也就是说,可以使用表达式ptd[0]访问该块的首元素,ptd[1]访问第2个元素,以此类推。

现在,我们有3种创建数组的方法:

  • 声明数组时,用常量表达式表示数组的维度,用数组名访问数组的元素。可以用静态内存或自动内存创建这种数组。
  • 声明变长数组(C99新增的特性)时,用变量表达式表示数组的维度,用数组名访问数组的元素。具有这种特性的数组只能在自动内存中创建。
  • 声明一个指针,调用malloc(),将其返回值赋给指针,使用指针访问数组的元素。该指针可以是静态的或自动的。

使用第2种和第3种方法可以创建动态数组(dynamic array))。这种数组和普通数组不同,可以在程序运行时选择数组的大小和分配内存。

在C99之前,不能这样做:

double item[n]; /* C99之前:n不允许是变量 */

但是,可以这样做,这比变长数组更灵活。

ptd = (double *) malloc(n * sizeof(double)); /* 可以 */

free()函数

通常,malloc()要与free()配套使用。

free()函数的参数是之前malloc()返回的地址,该函数释放之前malloc()分配的内存。因此,动态分配内存的存储期从调用malloc()分配内存到调用free()释放内存为止。

设想malloc()free()管理着一个内存池。每次调用malloc()分配内存给程序使用,每次调用free()把内存归还内存池中,这样便可重复使用这些内存。

不能用free()释放通过其他方式(如,声明一个数组)分配的内存。

malloc()free()的原型都在stdlib.h头文件中。

free不能释放同一块内存两次。

下面是malloc的使用例子:

/* dyn_arr.c -- 动态分配数组 */
#include <stdio.h>
#include <stdlib.h> /* 为 malloc()、free()提供原型 */

int main(void)
{
    double * ptd;
    int max;
    int number;
    int i = 0;

    puts("What is the maximum number of type double entries?");
    if (scanf("%d", &max) != 1) {
        puts("Number not correctly entered -- bye.");
        exit(EXIT_FAILURE);
    }
    ptd = (double *) malloc(max * sizeof(double));
    if (ptd == NULL) {
        puts("Memory allocation failed. Goodbye.");
        exit(EXIT_FAILURE);
    }
    /* ptd 现在指向有max个元素的数组 */
    puts("Enter the values (q to quit):");
    while (i < max && scanf("%lf", &ptd[i]) == 1){
        ++i;
    }


    printf("Here are your %d entries:\n", number = i);
    for (i = 0; i < number; i++) {
        printf("%7.2f ", ptd[i]);
        if (i % 7 == 6)
            putchar('\n');
    }
    if (i % 7 != 0)
        putchar('\n');
    puts("Done.");
    free(ptd);

    return 0;
}

输出结果如下:

image-20220901205509073

注意,free()函数位于程序的末尾,它释放了malloc()函数分配的内存。free()函数只释放其参数指向的内存块。一些操作系统在程序结束时会自动释放动态分配的内存,但是有些系统不会。为保险起见,请使用free(),不要依赖操作系统来清理。

在函数末尾处调用free()可避免内存泄露的问题发生。

calloc()函数

分配内存还可以使用calloc(),典型的用法如下:

long * newmem;
newmem = (long *)calloc(100, sizeof (long));

malloc()类似,在ANSI之前,calloc()也返回指向char的指针;在ANSI之后,返回指向void的指针。如果要存储不同的类型,应使用强制类型转换运算符。

calloc()函数接受两个无符号整数作为参数(ANSI规定是size_t类型)。第1个参数是所需的存储单元数量,第2个参数是存储单元的大小(以字节为单位)。在该例中,long为4字节,所以,前面的代码创建了100个4字节的存储单元,总共400字节。

calloc()函数还有一个特性:它把块中的所有位都设置为0(注意,在某些硬件系统中,不是把所有位都设置为0来表示浮点值0)。

free()函数也可用于释放calloc()分配的内存。

存储类别和动态内存分配

存储类别和动态内存分配有何联系?我们来看一个理想化模型。可以认为程序把它可用的内存分为 3部分:一部分供具有外部链接、内部链接和无链接的静态变量使用;一部分供自动变量使用;一部分供动态内存分配。

静态存储类别所用的内存数量在编译时确定,只要程序还在运行,就可访问存储在该部分的数据。该类别的变量在程序开始执行时被创建,在程序结束时被销毁。

然而,自动存储类别的变量在程序进入变量定义所在块时存在,在程序离开块时消失。因此,随着程序调用函数和函数结束,自动变量所用的内存数量也相应地增加和减少。这部分的内存通常作为栈来处理,这意味着新创建的变量按顺序加入内存,然后以相反的顺序销毁。

动态分配的内存在调用malloc()或相关函数时存在,在调用free()后释放。这部分的内存由程序员管理,而不是一套规则。所以内存块可以在一个函数中创建,在另一个函数中销毁。正是因为这样,这部分的内存用于动态内存分配会支离破碎。也就是说,未使用的内存块分散在已使用的内存块之间。另外,使用动态内存通常比使用栈内存慢。

总而言之,程序把静态对象、自动对象和动态分配的对象存储在不同的区域。

标签:malloc,函数,ptd,free,分配内存,内存,数组
From: https://www.cnblogs.com/wwjj4811/p/16647905.html

相关文章

  • freeswitch的3XX重定向
      概述sip协议标准RFC3261中,对3XX重定向有明确的定义。freeswitch中如何使用3XXredirect的特性,如何落地,应用场景有哪些? 环境centos:CentOS release7.0(Fina......
  • FreeRTOS
    FreeRTOS新建模板小书匠一、任务1.任务状态2.中断2-1.三个重点寄存器2-2.临界区代码3.任务创建与使用3-1.创建任务3-1-1.常用宏定义3-2.退出任务3-3.开启任务调......
  • 如何通过free看懂内存的真实使用
    free命令是Liunx操作系统中对内存进行查看和监控的一个常用命令。我们可以直接执行free命令获取操作系统内存使用的相关数据,如下所示:[root@ufdb165~]#free......
  • CentOS 7下安装docker和FreeSWITCH-全过程
    具体步骤如下:准备工作:提前将安装包放在CentOS系统下(提前创建好文件夹,这里为:/home/freeswitch(后转移至/home/soft-fs),所需文件如下: 复制第1、3、4、5到本地计算机(这里路......
  • new与malloc的区别以及实现方法
    new和malloc的内存分配在哪分配在堆上。也有说new是分配在自由存储区而malloc分配在堆上,自由存储区可以是堆也可以不是,具体要看new内部的实现。操作系统在堆上维护一个......
  • Native memory allocation (malloc) failed to allocate
    18:31:54.426WARNcom.zaxxer.hikari.pool.PoolBase.isConnectionAlive()@184-HikariPool-6-Failedtovalidateconnectioncom.mysql.cj.jdbc.ConnectionImpl@21......
  • 基于.NET6、FreeSql、若依UI、LayUI、Bootstrap构建插件式的CMS
    近几年,.net生态日益强大,特别是跨平台技术,性能提升,那真的是强大无比。为了日常能够快速开发,笔者基于基于.NET6、FreeSql、若依UI、LayUI、Bootstrap构建插件式的CMS,请大家......
  • FreeRec
    目录datadatasetsRecDataSetPostprocessor[TODO]fieldsFieldTokenizer[TODO]preprocessingtagsutils[TODO]layerscrossmodelsRecSysArchcriterionsRegularizer[TODO]Ba......
  • C#.NET ORM FreeSql 读取使用 US7ASCII 的 Oracle 数据库中文显示乱码问题
    ......
  • FreeSql笔记记录
    FreeSql的系列操作:freesql的操作有点类似于linq中的操作,不过freesql的操作是对数据库,但是linq的操作是对集合进行操作查看官方文档:https://freesql.net/guide/ 首先要......