c程序动态分配存储空间的方法

一、前言

本文将介绍如何动态分配存储空间,重点介绍c标准中动态分配存储空间的函数:malloc、calloc、realloc,以及使用alloc的注意事项,最后将介绍alloc类函数的替代接口。

二、malloc

相信大家对malloc函数并不陌生,malloc用于分配指定字节数的存储区,此存储区中的初始值是不确定的。

头文件:stdlib.h 函数原型:void* malloc(size_t size)
传入参数:所需申请的字节数
返回值:指向申请到的内存块的指针,如果分配失败,返回NULL

参考代码如下:

/*************************************************************************
        > File Name: alloc_like_test.c
        > Author: conbiao
        > Created Time: 2024年09月11日 星期三 14时02分16秒
 ************************************************************************/

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

/************************************************************************
 *                            MACRO
 ************************************************************************/
#define RING_BUFFER_SIZE 20

/************************************************************************
 *                              MAIN
 ************************************************************************/
int main(int argc, char *argv[])
{
    int *ring_buffer = NULL;
    int *buffer_prt = NULL;
    int i = 0;
    int num = 0;

    ring_buffer = malloc(sizeof(int) * RING_BUFFER_SIZE);
    if(!ring_buffer)
    {
        printf("%s: malloc failed!\n",__func__);
        return -1;
    }

    buffer_prt = ring_buffer;

    while(1)
    {
        if(num == 65536)
        {
            num = 0;
        }

        if(i == RING_BUFFER_SIZE - 1)
        {
            printf("%s: arrive the end of buffer,start from the begin!\n",__func__);
            *buffer_prt = num;
            buffer_prt = ring_buffer;
            i = 0;
        }
        else
        {
            *buffer_prt = num;
            buffer_prt++;
            i++;
        }

        num++;

        for(int j = 0; j < RING_BUFFER_SIZE - 1; j++)
        {
            printf("%s: ring_buffer[%d] = %d\n",__func__,j,ring_buffer[j]);
        }

    }

    free(ring_buffer);

    return 0;
}

上面的代码动态申请了一段20个int型大小的内存,作为一个环形buffer一直往里面填数据,运行结果如下:
在这里插入图片描述

(2-1)

三、calloc

calloc为指定数量和大小的内存块分配空间,并将其初始化为0.

头文件:stdlib.h 函数原型:void* calloc(size_t num, size_t size);
传入参数: num:数量
size:大小 返回值:指向申请到的内存块的指针,如果分配失败,返回NULL

参考代码如下:

/*************************************************************************
        > File Name: calloc_test.c
        > Author: conbiao
        > Created Time: 2024年09月11日 星期三 16时31分19秒
 ************************************************************************/

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


/************************************************************************
 *                               MAIN
 ************************************************************************/
int main(int argc, char *argv)
{
    char *malloc_ptr = NULL;
    char *calloc_ptr = NULL;

    malloc_ptr = malloc(sizeof(char) * 10);
    if(malloc_ptr == NULL)
    {
        printf("%s: malloc memory failed!\n",__func__);
        return -1;
    }
    else
    {
        for(int i = 0; i < 10; i++)
        {
            printf("malloc_ptr[%d]: %d\n",i,malloc_ptr[i]);
        }
    }

    calloc_ptr = calloc(10,sizeof(char));
    if(calloc_ptr == NULL)
    {
        printf("%s: malloc memory failed!\n",__func__);
        return -1;
    }
    else
    {
        for(int i = 0; i < 10; i++)
        {
            printf("calloc_ptr[%d]: %d\n",i,calloc_ptr[i]);
        }
    }

    return 0;
}

运行结果如下:
[图片]

(3-1)
如上图所见,malloc和calloc如果不做任何初始化,其默认的值都是0;

四、realloc

realloc用于重新分配之前有malloc、calloc、realloc分配的内存块的大小。如果新的内存区大小大于原大小,新增的内容并未初始化,且可能需要在新的内存区开辟空间(如果原内存区后空间不足的话)。如果新大小小于原大小,原内存块的尾部会被截断。

头文件:stdlib.h 函数原型: void* realloc(void* ptr, size_t size);
传入参数:
ptr:原来申请的内存块的指针 size:新内存块的大小
返回值:指向申请到的内存块的指针,如果分配失败,返回NULL

参考代码如下:

/*************************************************************************
        > File Name: realloc_test.c
        > Author: conbiao
        > Created Time: 2024年09月11日 星期三 17时02分31秒
 ************************************************************************/

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

/***********************************************************************
 *                               MACRO
 ***********************************************************************/
#define ORIGINAL_SIZE 10


/***********************************************************************
 *                               MAIN
 ***********************************************************************/
int main(int argc, char *argv[])
{
    int *malloc_ptr = NULL;
    int *realloc_ptr = NULL;
    int num = 0;

    if(argc != 2)
    {
        printf("%s: parameter number invalid! Please enter 2 params!\n",__func__);
        return -1;
    }

    if(!isdigit(*argv[1]))
    {
        printf("%s: paremeter invalid, please enter number!\n",__func__);
        return -1;
    }


    num = atoi(argv[1]);

    malloc_ptr = malloc(sizeof(int) * ORIGINAL_SIZE);
    if(malloc_ptr == NULL)
    {
        printf("%s: malloc memory failed!\n",__func__);
        return -1;
    }
    else
    {
        realloc_ptr = malloc_ptr;
        for(int i=0; i < num; i++)
        {
            if(i == 10)
            {
                realloc_ptr = realloc(realloc_ptr,sizeof(int) * num);
            }
            realloc_ptr[i] = i;

        }

        for(int i = 0; i < num; i++)
        {
            printf("%s: realloc_ptr[%d] = %d\n",__func__,i,realloc_ptr[i]);
        }
    }

    return 0;
}

运行结果如下图所示:
[图片]

(4-1)

五、free

函数free用于释放alloc函数申请到的内存空间,被释放的空间通常放入到可用存储区池,后续可在调用alloc函数的时候再分配。
需要注意的一点是,在调用realloc函数的时候,如果需要重新申请的存储区过大,需要新开辟空间,那么原先的空间realloc会自动释放掉,无需开发者手动释放,如果开发者手动释放的话会出现因为访问已经释放的内存而报段错误。
free的函数原型如下:

void free(void *ptr);

六、alloc函数相关注意事项

大多数分配内存空间的函数所分配的内存空间比所要求的要稍微大一些,而外的空间用来记录管理信息,比如分配块的长度、指向像一个分配块的指针等。这意味着如果对申请到的内存块以外的位置进行读写操作,会影响到其他内存块,这样会导致极其严重的问题。

6.1 内存泄漏

简单来说,内存就是在程序中通过alloc申请了内存,但是在不用了后没有free掉,导致进程的可用空间不断减少。(正常来说一个进程在被创建的时候其可用空间就已经分配好了,在运行过程中通过使用时申请,不用时释放使进程的可用空间保持在充裕的范围内。)这就是内存泄漏。
当然,内存泄漏只在一个进程还在运行时会产生问题,当一个进程结束时,会自动回收进程的所有资源,包括内存。即使一个进程在alloc了内存后,在不用时没有显式的去free,在进程结束时还是会自动回收这块内存。

6.2 访问空指针

如果访问了空指针一般会报段错误,并使程序退出。

6.3 内存访问越界

如果访问申请内存之外的存储空间,可能短时间并不会有致命性的问题,但是可能对其他内存块进行的写操作可能会在不知道什么时候出现致命性的错误。

6.4 重复释放内存

对已经free掉的内存进行重复释放,也会使程序崩溃。

写了一个程序来测试以上的四种常见错误,源码如下:

/*************************************************************************
 > File Name: memory_error.c
 > Author: conbiao
 > Created Time: 2024年09月12日 星期四 17时36分02秒
 ************************************************************************/

/***********************************************************************
 *                             HEADER
 **********************************************************************/
#include<stdio.h>
#include<stdlib.h>

/***********************************************************************
 *                              MACRO
 **********************************************************************/


/***********************************************************************
 *                       FUNCTION DESCRIPTION
 **********************************************************************/
void use_null_pointer(void);
void print_enter_error(void);
void access_illegal_address(void);
void memory_leak(void);
void re_release_memory(void);

/***********************************************************************
* FUNCTION NAME: use_null_pointer()
 ***********************************************************************
*
* Summary:
*   This function use to test use null pointer.
*
* Params:
*   None.
*
* Return:
*   None.
*
***********************************************************************/
void print_enter_error(void)
{
    printf("%s: enter param error!\n\n \
    Please enter 1 param:\n\n \
    0.Use null pointer. \n\n \
    1.Accessing illegal address. \n\n \
    2.Memory leak. \n\n \
    3.Re-release memory. \n\n",__func__);
}

/***********************************************************************
* FUNCTION NAME: use_null_pointer()
 ***********************************************************************
*
* Summary:
*   This function use to test use null pointer.
*
* Params:
*   None.
*
* Return:
*   None.
*
***********************************************************************/
void use_null_pointer(void)
{
    char *ptr = NULL;
    printf("%s: test start!\n",__func__);

    printf("%c\n",*ptr);

}


/***********************************************************************
* FUNCTION NAME: access_illegal_address()
 ***********************************************************************
*
* Summary:
*   This function use to test access illegal address
*
* Params:
*   None.
*
* Return:
*   None.
*
***********************************************************************/
void access_illegal_address(void)
{
    int *buffer = calloc(10,sizeof(int));

    printf("%s: test start!\n",__func__);
    //int buffer[10] = {0};
    if(buffer)
    {
        for(int i = 0; i <10; i++)
        {
            buffer[i] = i;
        }

        for(int i = 0; i <= 10; i++)
        {
            printf("%s: buffer[%d] = %d\n",__func__,i,buffer[i]);
        }

       free(buffer);

    }

}

/***********************************************************************
* FUNCTION NAME: memory_leak()
 ***********************************************************************
*
* Summary:
*   This function use to test memory leak.
*
* Params:
*   None.
*
* Return:
*   None.
*
***********************************************************************/
void memory_leak(void)
{
    int i = 0;

    printf("%s: test start!\n",__func__);

    while(1)
    {
        char *buffer = calloc(1000,sizeof(char));
        //printf("%s: %d\n",__func__,i++);
    }
}

/***********************************************************************
* FUNCTION NAME: re_release_memory()
 ***********************************************************************
*
* Summary:
*   This function use to test re-release memory.
*
* Params:
*   None.
*
* Return:
*   None.
*
***********************************************************************/
void re_release_memory(void)
{
    char *ori_buffer = NULL;
    char *new_buffer = NULL;

    printf("%s: test start!\n",__func__);

    ori_buffer = calloc(10,sizeof(char));

    if(ori_buffer)
    {
        new_buffer = realloc(ori_buffer,100);
    }

    if(new_buffer)
    {
        free(new_buffer);
        printf("%s: new_buffer release success.\n",__func__);
    }

    if(ori_buffer)
    {
        free(ori_buffer);
        printf("%s: ori_buffer release success.\n",__func__);
    }
}

/***********************************************************************
 *                                MAIN
 **********************************************************************/
//1.Use null pointer
//2.Accessing illegal address
//3.Memory leak
//4.re-release memory

int main(int argc, char *argv[])
{
    int ret = 0;

    if(argc != 2)
    {
        print_enter_error();
        return -1;
    }

    int enter = atoi(argv[1]);

    if(enter == 0)
    {
        use_null_pointer();
    }
    else if(enter == 1)
    {
        access_illegal_address();
    }
    else if(enter == 2)
    {
        memory_leak();
    }
    else if(enter == 3)
    {
        re_release_memory();
    }
    else
    {
        print_enter_error();
    }


    return ret;
}

运行结果如下:
[图片]

(6-1)

七、其他alloc函数

在不同系统上,实现了很多其他用于分配存储空间的函数,比如:libmalloc、vmalloc、quick-fit、jemalloc、TCMalloc、alloca。
有兴趣的可以自行去研究这些函数。

参考资料:

《UNIX环境高级编程(第3版) (史蒂文斯 (W.Richard Stevens) 拉戈 (Stephen A.Rago))
(Z-Library)》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值