美文网首页
C语言-动态分配内存 malloc & free

C语言-动态分配内存 malloc & free

作者: 我阿郑 | 来源:发表于2021-12-13 10:48 被阅读0次

需要用一个数组来保存用户的输入,但是却不知道用户会输入多少条数据。

(1) 如果设一个太大的数组,则显得浪费内存
(2) 如果设得太小,又怕不够

问题:如何做到恰好够用、又一点不浪费呢?

C/C++里,要求数组长度为常量

int Contact[100]; // 长度必须在代码里固定

// 不能这样
int n = 0;
scanf(“%d”, &n);
int Contact[n]; // 编译错误!!数组长度必须为常量❌

动态内存分配 – malloc/free

系统中存在一个内存管理器(MM, Memory Manager),它负责管理一堆闲置内存。它被设计用于解决此类问题。

MM提供的服务:应用程序可以向MM申请(借出)一块指定大小的内存,用完之后再释放(还回)。

例如:
void* ptr = malloc (1024); // 申请,从MM借出内存
free(ptr); // 释放,还回MM
  • malloc函数申请的内存空间要用一个指针类型的变量指向其首地址
  • 上面在堆内存开辟了1024个字节

1、malloc函数

// 参数size: 指定要申请的内存空间的大小
// 返回值: void* ,指向这一块内存地址
// (MM不关心你拿这块内存来存储何种数据,所以返回void*)
void* malloc(int size)

应用程序在使用malloc时,要把返回值转换成目标类型。

// 例如,要申请一块空间存放1000个Contact对象,则:

int size = 1000 * sizeof(Contact);
Contact* p = (Contact*) malloc(size);

这块内存和数组没有本质区别,用法完全相同。

2、free函数

// ptr: 先前malloc返回的内存地址
// 返回值: void* ,指向这一块内存地址
void free(void* ptr)

需要先计算需要多少字节的内存空间

// 举例: 
char* p = (char*)malloc(8); // 申请8个字节
for(int i=0; i<8; i++)
{
   p[i] = i + 1;
}
free(p); // 释放 (只需要释放p,不要被循环误导)

// 举例:
int size = 4 * sizeof(Contact);
Contact* p = (Contact*) malloc (size);
p[0].id = 1;
strcpy(p[0].name, “shaofa”);

free (p);

// 举例:
// 用户自己决定要输入多少条记录
int n = 0;
scanf(“%d”,&n); 

// 用户需要多少,就分配多少内存
int size = n * sizeof(Contact);
Contact* p = (Contact*) malloc(size);

// 释放
free(p);

数组举例子:

double * pdoubles[10]; // 定义要一个指针数组
for(int i=0; i<10; i++) {
    pdoubles[i] = malloc(sizeof(double));
    if(pdoubles[i] == NULL) { // 判断是否成功分配
        printf("report malloc error");
    }
}

// 释放分配的内存
for(int i=0; i<10; i++) {
    if(pdoubles[i] != NULL) {
       free(pdoubles[i])
    }
}

释放的时候需要注意, 因为在for循环执行之后,p的地址往前移动了10, 所以需要减去10, 然后再释放p,不然会有问题

char *p = (char *) malloc(4);
p[0] = 10;
p[1] = 11;
p[2] = 12;

// 等价
*p = 10
*(p+1) = 11
*(p+2) = 12

// 当销毁时只需要free一次,malloc了几个字节就会free几个字节,和char类型还是int类型无关
free(p);

在一个函数中动态分配的内存,在另一个函数中操作这块内存

void func(int *q) {
    *q = 200;
}

void func2() {
    int *p = (int *)malloc(sizeof(int));
    *p = 10;
    
    printf("%d\n",*p); // 10
    func(p);
    printf("%d\n",*p); // 200
    free(p);
}

关于MM

(1) MM是一个系统级的东西,所有的应用程序都向同一个MM申请内存。

(2) 何为借出?实际上,在内存被借出时,MM只是把它管理的内存标记了一下,表示该段内存已经被占用。比如,它把每一段被占用的内存给记录下来(首地址,长度)
(p0,n0) (p1, n1) (p2, n2) ...

(3) MM非常慷慨:①只要有人malloc,它都同意借出 ②你不归还,它永远不会主动要求你free

(4) MM管理的内存区域称为“堆”Heap

这意味着,用户程序应该自觉得及时free,以便不耽误别的应用程序的使用。如果有个应用程序不停地malloc,而不free,那最终会用光MM的内存。当MM没有更多闲置内存时,malloc返回NULL,表示内存已经用完。

再次重申:应用程序在malloc之后,应该尽早free!

使用原则:需要的时候再申请,不需要的时候立即释放

为何free的时候只需一个首地址呢?为什么不传递长度?

实际上,MM对借出的内存块进行标识
(p0, n0) (p1, n1) (p2, n2) ...
它内部已经保证任意两块内存不会“交叠”,即不会重叠,不会把一块内存同时借给两个应用程序使用。

所以,每块内存的首地址都是不同的,在free的时候只需要指明首地址即可。

何为“对象”

对象指的一块内存

// a是一个对象,即存放着一个对象的数据
Contact a;  

// 确切地说:p指向了一个对象
Contact* p = (Contact*) malloc(sizeof(Contact));

示例:用Citizen表示一个市民,用Car表示一个辆车。他起初没有车,但未来可能有一辆车。

struct Car
{
    char maker[32]; // 制造商
    int  price;  // 价格
};
struct Citizen
{
    char name[32]; // 名字
    int  deposite; // 存款
    Car* car;  // NULL时表示没车
};

// 定义一个对象,开始没车
Citizen shaofa = { “shaofa”, 100, NULL };

// 后来,他可能买了一辆车
void buy(Citizen* owner)
{ 
    // 创建一个对象
    Car* car = (Car*) malloc(sizeof(Car));
    strcpy(car->maker, “Chevrolet”);
    car->price = 10;

    // 保存此对象 (确切地说是记住了指针)
    owner->car = car; // 有车了
    owner->deposite -= car->price; // 钱没了
}

// 终有一天,这车会报废...
void discard(Citizen* who)
{
    free(who->car); // 此对象被销毁
    who->car = NULL;  // 回到无车状态   
}

// 也有可能会买给别人
void sell(Citizen* owner, Citizen* other)
{
    Car* car = owner->car;
    car->price *= 0.5; //半价出售
    other->car = car; // 别人拥有了这辆车

    owner->deposite += car->price; // 收回一部分成本
    //free(car); // oh,no! 不能free,这车在别人手里 ❌
    owner->car = NULL;  // 回到无车状态   
}

注意事项

1、不是malloc的指针,不可以free

例如:
  int a = 10;
  int* p = a;
  free (p ); // 开什么玩笑??这个指针根本不是从MM那里借来的

2、malloc的内存,必须及时free

怎么样才算“及时”? “不及时”会怎样?

MM里可用的内存是有限的,你用完了就得尽快还,因为别的应用程序也需要MM的内存。

只借不还,积累到一定程度,MM没有更多内存可用,于是malloc返回NULL。

while(1)
{
   void* ptr = malloc(1024*512);
}

3、要free必须free首地址

错误的例子:
char* p = (char*) malloc(100); // 100个字节
free (p+50); // 借了100, 只还50?

要还就得全还,否则MM那边处理不了

4、malloc的返回值需要检测

char* ptr = (char*) malloc(1024); // 512K
if(ptr != NULL)
{
    ...
}

原因是:MM可能此时没有闲置内存可用。(虽然这种情况一般不会发生)

5、free之后,该指针不应再使用

free之后,该内存交还给MM,该内存不再可用(失效)

// 以下代码是错误的:
char* p = (char*) malloc(100); 
free (p); 
for(int i=0; i<100; i++)
{
   p[i] = i; //该内存已经被free,绝不可继续使用!
}

// 良好的编程习惯是:
free(p);
p = NULL;  // 置为空指针

6、malloc得到的内存,可以任意位置释放

不一定要在相同的函数里释放,在应用程序的任意一个角落释放都是有效的。

也就是说:这一块内存被malloc出来之后,完全交给你处置

内存处理函数

1、memset()

void *memset(void *s, int ch, size_t n);

功能:将s中当前位置后面的n个字节 (typedef unsigned int size_t )用 ch 替换并返回 s

参数:

s:需要操作内存s的首地址
ch:填充的字符,ch虽然参数为int,但必须是unsigned char, 范围为 0-255
n:指定需要设置的大小
返回值:s的首地址

例如:
int *p = (int *)malloc(sizeof(int) * 10);
memset(p, 0, 40); // 重置内存空间的值

2、 memcpy()

void *memcpy(void *dest, void *src, unsigned n);

参数:

dest: 指向用于存储复制内容的目标数组,类型强制转换为 void* 指针。
src: 指向要复制的数据源,类型强制转换为 void* 指针。
n: 要被复制的字节数

返回值
该函数返回一个指向目标存储区dest的指针

3、 memmove()

void *memmove( void* dest, const void* src, size_t n);

功能:由src所指内存区域复制n个字节到dest所指内存区域。

memmove()功能用法和memcpy())一样,区别在于:dest
src所指的内存空间重叠时,memmove()仍然能处理,不过执行效率比memcpy()低一些

相关文章

  • C++ new 和 delete 运算符

    在 C 语言中,动态分配内存用 malloc () 函数,释放内存用 free () 函数。如下所示: 在 C++...

  • C语言的malloc

    为什么C语言要有malloc malloc就是memory allocate动态分配内存,malloc的出现时为了...

  • C语言-动态分配内存 malloc & free

    需要用一个数组来保存用户的输入,但是却不知道用户会输入多少条数据。 (1) 如果设一个太大的数组,则显得浪费内存(...

  • c++动态分配浅析

    1. c语言中动态分配和释放. 在c中,申请动态内存是使用malloc和free,这两个函数是c的标准库函数,分配...

  • GC基础

    1.什么是垃圾 C语言申请内存:malloc free C++: new delete c/C++ 手动回收内存 ...

  • c++之内存相关

    堆空间内存控制 malloc \ free C语言模式内存申请和释放 new \ delete ...

  • malloc和New的区别

    1、malloc/free是c中的一个函数,new/delet是c++中的操作符,都是动态分配内存 2、void ...

  • 阅读游戏引擎架构

    通过malloc()/free()或C++的全局new/delete运算符动态分配内存——又称为堆分配——通常是非...

  • C语言:关于FREE()函数的用法

    C语言中,malloc, alloc, free是c定义的一组内存管理的API函数,free可以释放call...

  • C++笔试整理

    1、new 、delete 、malloc 、free 的关系malloc 与 free 是 C++/C 语言的标...

网友评论

      本文标题:C语言-动态分配内存 malloc & free

      本文链接:https://www.haomeiwen.com/subject/tylrfrtx.html