内存管理函数

本文详细介绍了C语言中的动态内存管理函数,包括malloc用于分配内存,free用于释放内存,calloc用于分配并初始化为零的内存,以及realloc用于调整已分配内存的大小。同时,文章强调了内存泄漏的概念及其危害,并通过示例代码展示了如何避免内存泄漏。最后,探讨了栈区和堆区的区别以及内存分配的细节。
摘要由CSDN通过智能技术生成

1、动态内存申请的API

NAME
       malloc, free, calloc, realloc - allocate and free dynamic memory

SYNOPSIS
       #include <stdlib.h>

       void *malloc(size_t size);  
       void free(void *ptr);
       void *calloc(size_t nmemb, size_t size);
       void *realloc(void *ptr, size_t size);

DESCRIPTION
       The  malloc()  function allocates size bytes and returns a pointer to the allocated memory.  The memory is not
       initialized.  If size is 0, then malloc() returns either NULL, or a unique pointer value  that  can  later  be
       successfully passed to free().

       The free() function frees the memory space pointed to by ptr, which must have been returned by a previous call
       to malloc(), calloc(), or realloc().  Otherwise, or if free(ptr) has already  been  called  before,  undefined
       behavior occurs.  If ptr is NULL, no operation is performed.

       The calloc() function allocates memory for an array of nmemb elements of size bytes each and returns a pointer
       to the allocated memory.  The memory is set to zero.  If nmemb or size is  0,  then  calloc()  returns  either
       NULL, or a unique pointer value that can later be successfully passed to free().

       The  realloc()  function  changes  the size of the memory block pointed to by ptr to size bytes.  The contents
       will be unchanged in the range from the start of the region up to the minimum of the old and  new  sizes.   If
       the  new size is larger than the old size, the added memory will not be initialized.  If ptr is NULL, then the
       call is equivalent to malloc(size), for all values of size; if size is equal to zero, and  ptr  is  not  NULL,
       then  the  call is equivalent to free(ptr).  Unless ptr is NULL, it must have been returned by an earlier call
       to malloc(), calloc() or realloc().  If the area pointed to was moved, a free(ptr) is done.

malloc申请堆区空间

在这里插入图片描述

 #include <stdlib.h>

       void *malloc(size_t size);   
       //因为不知道要申请的是哪种类型的空间(char、int等等),所以使用万能指针方便匹配,得到系统分配的空间起始地址  
       //size代表的是要申请的字节数
       //函数的返回值:
              //成功:返回空间的起始地址
              //失败:返回NULL

free释放堆区空间

#include <stdlib.h>
void free(void *ptr);     //ptr需要释放的堆区空间的起始地址

malloc和free的使用

#include <stdio.h>
#include <stdlib.h>
void test()
{
    int *p = NULL;
    p = (int *)malloc(sizeof(int)); // 返回一个万能指针,因为malloc返回值是一个地址,为 void *,所以要将void *强转为 int *
    if (p == NULL){
        return;       //防止发生段错误
    }
    *p = 100;
    printf("*p = %d\n", *p);
    free(p); // 释放堆区空间
}
int main(int argc, char const *argv[])
{
    test();
    return 0;
}

在这里插入图片描述

申请堆区空间之后一定要释放

malloc函数的特点

malloc申请的堆区空间不自动清零 ,也就是说申请的堆区空间中还存有上次申请保存的数据,如果这样操作申请的堆区空间,那么操作得到的结果是不确定的

memset内存设置函数

在这里插入图片描述
s 是空间起始地址
c 是每一个字节需要填充的值
n 是空间的字节宽度

malloc申请数组空间

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

void test()
{
    int n = 0;
    printf("请输入 int 类型元素的个数:");
    scanf("%d", &n);
    int *p = NULL;
    // 申请堆区空间
    p = (int *)malloc(n * sizeof(int)); // 返回一个万能指针,因为malloc返回值是一个地址,为 void *,所以要将void *强转为 int *
    if (p == NULL)
    {
        // 防止申请失败
        return;
    }
    memset(p, 0, n * sizeof(int)); // 手动清零,将每一个元素设置为 0
    printf("请输入 %d 个元素:", n);
    for (int i = 0; i < n; i++)
    {
        scanf("%d", (p + i));
    }
    for (int i = 0; i < n; i++)
    {
        printf("%d\t", *(p + i));
    }

    free(p); // 释放堆区空间
    printf("\n");
}

int main(int argc, char const *argv[])
{
    test();
    return 0;
}

在这里插入图片描述

calloc函数

#include <stdlib.h>
 void *calloc(size_t nmemb, size_t size);
 //nmemb:内存的块数
 //size:每一块的字节数,所以是一个总的地址连续空间,为 nmemb * size
 //返回值:成功是堆区空间的起始地址,失败为NULL
 //mallo会对申请的空间自动清零

在这里插入图片描述

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

void *get_addr(int n, int elem_size)
{
    // 堆区空间申请
    return calloc(n, elem_size);
}

void input_int_arr(int n, int *p)
{
    printf("请输入 %d 个元素:", n);
    for (int i = 0; i < n; i++)
    {
        scanf("%d", (p + i));
    }
}

void printf_int_arr(int n, int *p)
{
    for (int i = 0; i < n; i++)
    {
        printf("%d\t", *(p + i));
    }
}
void test()
{
    int n = 0;
    printf("请输入 int 类型元素的个数:");
    scanf("%d", &n);
    int *p = NULL;
    // 申请堆区空间
    p = (int *)get_addr(n, sizeof(int)); // 返回一个万能指针,因为malloc返回值是一个地址,为 void *,所以要将void *强转为 int *
    if (p == NULL)
    {
        // 防止申请失败
        return;
    }
    //获取键盘输入
    input_int_arr(n, p);
    //输入数组
    printf_int_arr(n, p);
    free(p); // 释放堆区空间
    printf("\n");
}

int main(int argc, char const *argv[])
{
    test();
    return 0;
}

在这里插入图片描述

realloc 函数 追加空间

追加空间只能对堆区空间追加,也即是只能对 mollc和calloc申请的堆区进行追加(追加是既可以减少也可以增加)

#include <stdlib.h>
void *realloc(void *ptr, size_t size);

在这里插入图片描述
必须使用指针变量 获取 realloc 的返回值(返回值是新开辟空间的起始地址)

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

void *get_addr(int n, int elem_size)
{
    // 堆区空间申请
    return calloc(n, elem_size);
}

void input_int_arr(int n, int *p)
{
    printf("请输入 %d 个元素:", n);
    for (int i = 0; i < n; i++)
    {
        scanf("%d", (p + i));
    }
}

void printf_int_arr(int n, int *p)
{
    for (int i = 0; i < n; i++)
    {
        printf("%d\t", *(p + i));
    }
}

void realloc_fun(int n, int *p)
{
    printf("\n");
    printf("请输入要新增的元素个数:");
    int new_num = 0;
    scanf("%d", &new_num);
    // 追加空间
    p = (int *)realloc(p, (n + new_num) * sizeof(int));
    printf("请输入 %d 个新增加元素:", new_num);
    for (int i = n; i < n + new_num; i++)
    {
        scanf("%d", (p + i));
    }
    // 输出数组
    printf_int_arr(n + new_num, p);
}

void test()
{
    int n = 0;
    printf("请输入 int 类型元素的个数:");
    scanf("%d", &n);
    int *p = NULL;
    // 申请堆区空间
    p = (int *)get_addr(n, sizeof(int)); // 返回一个万能指针,因为malloc返回值是一个地址,为 void *,所以要将void *强转为 int *
    if (p == NULL)
    {
        // 防止申请失败
        return;
    }
    // 输入数组
    input_int_arr(n, p);
    // 输出数组
    printf_int_arr(n, p);
    // 追加空间
    realloc_fun(n, p);
    free(p); // 释放堆区空间
    printf("\n");
}

int main(int argc, char const *argv[])
{
    test();
    return 0;
}

void realloc_fun(int n, int *p)
{
    printf("\n");
    printf("请输入要新增的元素个数:");
    int new_num = 0;
    scanf("%d", &new_num);
    // 追加空间
    p = (int *)realloc(p, (n + new_num) * sizeof(int));
    printf("请输入 %d 个新增加元素:", new_num);
    for (int i = n; i < n + new_num; i++)
    {
        scanf("%d", (p + i));
    }
    // 输出数组
    printf_int_arr(n + new_num, p);
}

在这里插入图片描述

2、内存泄露

申请的内存,首地址丢了,找不了,再也没法使用,也没法释放了,这叫做内存泄露

案例一

在这里插入图片描述

案例二

在这里插入图片描述
函数结束,局部指针变量要释放,但是堆区空间此时还没有释放,所以造成了没有人知道申请的堆区空间了,所以在函数调用结束的时候要人为释放堆区空间或者通过返回值知道堆区空间的起始地址吗,一直使用,不让丢失。每调用一次泄露一次

3、内存回顾

// 64位平台
void test05()
{
    // 字符数组 在栈区 开辟12字节 存放hello world
    char str[] = "hello world";
    // 字符指针变量 在栈区 4B 保存文字常量区 hello world的首元素地址
    char *str1 = "hello world";
    // 字符指针变量 在栈区 4B 保存堆区空间的起始位置
    char *str2 = (char *)malloc(128);
    // 将字符串拷贝到str2指向的堆区空间
    strcpy(str2, "Hello world");
    printf("str2 = %s\n", str2);
}
char a;
int b;
// void fun(char str4[128])
void fun(char *str4)
{
    printf("%lu\n", sizeof(str4)); // 将字符数组优化为指针,所以为 4
    return;
}
void test06()
{
    char str[] = "hello";
    char *str1 = "hello";
    char *str2 = (char *)malloc(128);
    printf("%lu\n", sizeof(a));    // 1
    printf("%lu\n", sizeof(b));    // 4
    printf("%lu\n", sizeof(str));  // 6
    printf("%lu\n", sizeof(str1)); // 8  指针变量
    printf("%lu\n", sizeof(str2)); // 8  指针变量
    fun(str);
}
int main(int argc, char const *argv[])
{
    test05();
    test06();
    return 0;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Q渡劫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值