c 动态数组内存分配

这篇博客详细介绍了C语言中的动态内存管理函数,包括calloc()、malloc()、realloc()和free()。文章强调了在使用realloc()时需要注意的安全问题,如内存泄漏和双重释放,并提供了示例代码来展示动态数组的分配和释放过程。
摘要由CSDN通过智能技术生成
// 一维动态数组
 
/**
    calloc(配置内存空间)
    相关函数 malloc, free, realloc, brk
    表头文件 #include <stdlib.h>
    定义函数 void *calloc(size_t nmemb, size_t size)
    函数说明 calloc() 用来配置nmemb个相邻的内存单元,每一单位的大小为size, 并返回指向第一个元素的指针。 
    这和使用下列的方式效果相同: malloc(nmemb*size); 不过, 在利用calloc()配置内存时会将内存初始化为0。
    返回值 若配置成功则返回一指针,失败则返回NULL。

    free(释放原先配置的内存)
    相关函数 malloc, calloc, realloc, brk
    表头文件 #include <stdlib.h>
    定义函数 void free(void *ptr)
    函数说明 参数ptr为指向先前由malloc(), calloc() 或 realloc() 所返回的内存指针。
    调用free()后ptr所指向的内存空间便会被收回。假若参数ptr所指的内存空间已被收回或是未知的内存地址,则调用
    free()可能会有无法预期的情况发生。若参数ptr为NULL, 则free()不会有任何作用。
*/
#include <stdio.h>
#include <stdlib.h>
int main()
{
    int n1, i;
    int *array;
    printf("请输入所要创建的一维动态数组的长度: ");
    scanf("%d", &n1);
   
    //array = calloc(sizeof(int), n1);
    array = (int *)malloc(n1 * sizeof(int));
    for(i = 0; i<n1; ++i)
    {
        printf("%d\t", array[i]);
    }
    printf("\n");
    for(i = 0; i<n1; ++i)
    {
        array[i] = i+1;
        printf("%d\t", array[i]);
    }
    printf("\n");
    free(array);
    return 0;    
}

 二维数组分配可以参考我的博客: #867 转置矩阵

动态内存分配

// 动态数组内存分配
/**
安全起见,小心使用C语言的 realloc()函数 
在 C 语言中, 良好的编程习惯要求一个函数只做一件事, 如果一个函数实现了若干功能, 可以说基本是一个糟糕的设计。
C语言 realloc()函数位于 stdlib.h头文件中,原型 void *realloc(void *ptr, size_t size);
realloc() 会将ptr所指向的内存块的大小修改为size, 并将新的内存指针返回。
设之前内存块的大小为n, 如果size < n, 那么截取的内容不会发生变化, 如果size > n, 那么新分配的内存不会被初始化
如果 ptr = NULL, 那么相当于调用 malloc(size); 如果size=0, 那么相当于调用free(ptr)。
如果 ptr 不为 NULL, 那么他肯定是由之前的内存分配函数返回的, 例如 malloc(), calloc() 或 realloc()。
如果 ptr 所指内存块被移动, 那么会调用free(ptr).

realloc可以对指定的指针所指的空间进行扩大或者缩小, 无论是扩张还是缩小, 原有内存中的内容将保持不变。 当然,
对于缩小, 则被缩小的那一部分内容会丢失。 realloc并不保证调整后的内存空间和原来的内存空间保持同一内存地址。 
相反, realloc返回的指针很有可能指向一个新的地址。

realloc是从堆上分配内存的。 当扩大一块内存空间时, realloc()试图直接从堆上现存的数据后面的那些字节中获取附加的字节,
如果能够满足,自然天下太平; 如果数据后面的字节不够, 问题就出来了, 那么就使用堆上的第一个有足够大小的自由块, 现存的
数据然后就被拷贝至新的位置, 而老块则放回到堆上, 这句话传递的一个重要的信息就是数据可能被移动。

综上,一个简单的realloc() 却被赋予了好几个功能, 这并不是良好的函数设计。 虽然在编码中, realloc()会提供一定的方便, 
但是也会很容易引发Bug.

>>> BUG Example:
>>> Example one:
void *ptr = realloc(ptr, new_size);
if(!ptr)
{
    // 错误处理
}

这里就引出了一个内存泄露的问题, 当realloc()分配失败的时候, 会返回NULL。 但是参数中的ptr的内存是没有被释放的。
如果直接将realloc()的返回值赋给ptr, 那么当申请内存失败时, 就会造成ptr原来指向的内存丢失,造成内存游离和泄露。

void *new_ptr = realloc(ptr, new_size);
if(!new_ptr)
{
    // 错误处理
}
ptr = new_ptr

>>> Example two:
实际上, malloc(0) 是合法的语句, 会返还一个合法的指针, 且该指针可以通过free去释放。 这就造成了很多人对realloc的错误理解,
认为当size为0时,realloc也会返回一个合法的指针

void *new_ptr = realloc(old_ptr, new_size);
// 其他代码
free(new_ptr)

由于错误的认识, 不去检验new_size是否为0, 还是按照new_size不为0的逻辑处理, 最后依然使用free(new_ptr)去释放。这里就引入了
double free的问题,造成程序崩溃。

所以, 大家在使用realloc的时候还是要十分小心。
*/
#include <stdio.h>
#include <stdlib.h>
int main()
{
    int *n, *p;
    int i, n1, n2;
    printf("请输入所要创建的动态数组的长度:");
    scanf("%d", &n1);
    n = calloc(sizeof(int *), n1);
    printf("请输入所要扩展的动态数组的长度: ");
    scanf("%d", &n2);
    p = (int *)realloc(n, sizeof(int)*n2);
    
    if(n2 > n1)
    {
        for(i = n1; i<n2; ++i)
        {
            p[i] = i+1; 
        }
    }
    // 测试新分配指针首地址是否发生移动
    if(p == n) 
    {
        printf("原指针附加地址足够, 首地址没移动! \n");
    }
    else 
    {
        printf("原指针堆后面附加地址不够,重新分配一块地址!\n");
    }
    // 开关继续执行,方便测试上面两行代码用。
    getchar(); 
    getchar();
    
    for(i = 0; i<n2; ++i)
    {
        if(i%5==0) printf("\n");
        printf("%d\t", p[i]);
    }
    printf("\n");
    free(p);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值