【C语言】(20)动态内存分配

动态内存分配是通过stdlib标准库函数来管理的,主要包括malloccallocreallocfree。这些函数允许在程序运行时分配和释放内存,使得内存的使用更加灵活。

1.动态内存分配函数

1.1 malloc

malloc函数用于分配一定数量的内存。它的原型在stdlib.h头文件中定义:

void* malloc(size_t size);
  • size:需要分配的内存字节数。
  • 返回值:成功时返回指向分配内存的指针;如果分配失败,返回NULL

示例

#include <stdlib.h>

int main() {
    int *p = malloc(10 * sizeof(int)); // 分配10个整数的空间
    if (p == NULL) {
        // 处理内存分配失败的情况
    }
    // 使用p...
    free(p);
    return 0;
}

1.2 calloc

calloc函数用于分配并初始化内存。它的原型也在stdlib.h头文件中定义:

void* calloc(size_t num, size_t size);
  • num:需要分配的元素数量。
  • size:每个元素的大小。
  • 返回值:成功时返回指向分配并初始化为0的内存的指针;如果分配失败,返回NULL

示例

#include <stdlib.h>

int main() {
    int *p = calloc(10, sizeof(int)); // 分配并初始化10个整数
    if (p == NULL) {
        // 处理内存分配失败的情况
    }
    // 使用p...
    free(p);
    return 0;
}

1.3 realloc

realloc函数用于重新分配内存。它可以增加或减少已分配内存的大小。其原型定义在stdlib.h头文件中:

void* realloc(void* ptr, size_t newSize);
  • ptr:指向先前分配的内存的指针。
  • newSize:新的内存大小。
  • 返回值:成功时返回指向新分配内存的指针;如果分配失败,返回NULL,原指针ptr仍然有效。

示例

#include <stdlib.h>

int main() {
    int *p = malloc(10 * sizeof(int)); // 最初分配10个整数的空间
    p = realloc(p, 20 * sizeof(int)); // 现在重新分配为20个整数的空间
    if (p == NULL) {
        // 处理内存分配失败的情况
    }
    // 使用p...
    free(p);
    return 0;
}

1.4 free

free函数用于释放先前通过malloccallocrealloc分配的内存。它的原型定义在stdlib.h头文件中:

void free(void* ptr);
  • ptr:指向需要释放的内存的指针。

注意:一旦内存被释放,指针ptr就不应再被访问。为了避免悬挂指针,建议将ptr设置为NULL

示例

#include <stdlib.h>

int main() {
    int *p = malloc(10 * sizeof(int));
    // 使用p...
    free(p);
    p = NULL; // 避免悬挂指针
    return 0;
}

2.动态内存分配的常见错误

2.1 未检查返回值

使用malloccalloc分配内存时,如果系统没有足够的内存可供分配,这些函数将返回NULL。不检查这些函数的返回值直接使用返回的指针,可能会导致程序解引用空指针而崩溃。

错误示例

int *ptr = malloc(sizeof(int) * 50); // 假设分配失败
*ptr = 5; // 如果ptr为NULL,这里会导致程序崩溃

正确做法

int *ptr = malloc(sizeof(int) * 50);
if (ptr == NULL) {
    // 处理内存分配失败的情况
}

2.2 忘记释放内存

每次调用malloccallocrealloc分配的内存,在不再需要时应该使用free函数释放。忘记释放内存会导致内存泄露,可能导致程序随着时间的推移而运行缓慢,并最终耗尽可用内存。

2.3 重复释放内存

对同一块内存调用free两次是未定义行为,可能会导致程序崩溃。

错误示例

int *ptr = malloc(sizeof(int));
free(ptr);
free(ptr); // 重复释放

2.4 使用已释放的内存

释放内存后继续使用该内存的指针,会导致未定义行为,通常称为悬挂指针(dangling pointer)问题。

错误示例

int *ptr = malloc(sizeof(int));
free(ptr);
*ptr = 10; // 使用已释放的内存

2.5 越界访问

动态分配的内存块有固定的大小,超出其边界的访问是未定义的行为,可能会导致数据损坏或程序崩溃。

2.6 错误的内存大小计算

在使用malloc等函数时,传递错误的大小给这些函数,可能会导致内存分配不足或过多,从而引起未定义行为或浪费资源。

2.7 忘记为realloc返回的新指针赋值

realloc函数用于调整已分配内存的大小。如果realloc成功,它会返回指向新内存的指针,可能与原来的指针不同。如果不更新指针,可能会导致访问旧指针和内存泄露。

错误示例

int *ptr = malloc(sizeof(int) * 2);
realloc(ptr, sizeof(int) * 4); // 忽略了realloc的返回值

正确做法

int *ptr = malloc(sizeof(int) * 2);
int *new_ptr = realloc(ptr, sizeof(int) * 4);
if (new_ptr != NULL) {
    ptr = new_ptr;
}

3.柔性数组

柔性数组成员(Flexible Array Member,FAM)提供了一种方便的方式来表示结构体末尾的可变长度数组。然而,柔性数组本身并不支持动态扩容,因为它们的大小在结构体实例被首次分配内存时就已经确定。要实现类似“动态扩容”的功能,你需要手动重新分配内存,并小心处理数据的复制和迁移。

基本思路

要“动态扩容”一个包含柔性数组的结构体,可以按照以下步骤操作:

  1. 确定新的大小:根据需要扩容的数量,计算新的总大小。
  2. 重新分配内存:使用realloc函数为原结构体实例分配更大的内存块。
  3. 检查分配结果:确保realloc成功,否则处理内存分配失败的情况。
  4. 更新结构体状态:如果扩容成功,更新结构体内部的状态,如元素计数。

注意

  • 使用realloc进行内存重新分配时,如果新的内存分配成功,原内存块的内容(直到较小的旧尺寸或新尺寸)会被自动复制到新内存块中,原内存块随后会被释放。
  • 如果realloc返回NULL,原内存块不会被释放。因此,在处理realloc失败时要小心,以避免内存泄漏。
  • 在实际使用中,应考虑如何处理数据迁移和默认值初始化等问题,尤其是扩容后新添加的元素。

示例

假设我们有以下包含柔性数组的结构体定义:

struct flex_array_struct {
    size_t count;
    int data[]; // 柔性数组成员
};

接下来,我们将通过一个函数来扩展这个结构体实例的data数组大小:

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

struct flex_array_struct* resize_flex_array(struct flex_array_struct* array, size_t new_count) {
    // 计算新的内存大小
    size_t new_size = sizeof(struct flex_array_struct) + sizeof(int) * new_count;
    
    // 尝试重新分配内存
    struct flex_array_struct* new_array = realloc(array, new_size);
    if (new_array == NULL) {
        // 内存分配失败,根据需要处理错误
        return NULL;
    }
    
    // 更新新数组的元素计数
    new_array->count = new_count;
    return new_array;
}

int main() {
    // 初始化并分配一个具有5个元素的柔性数组
    size_t initial_count = 5;
    struct flex_array_struct* my_array = malloc(sizeof(struct flex_array_struct) + sizeof(int) * initial_count);
    if (my_array == NULL) {
        // 处理内存分配失败
        return 1;
    }
    my_array->count = initial_count;
    
    // 动态扩容数组到10个元素
    size_t new_count = 10;
    my_array = resize_flex_array(my_array, new_count);
    if (my_array == NULL) {
        // 处理内存分配失败
        free(my_array);
        return 1;
    }
    
    // 使用扩容后的数组...
    
    // 释放内存
    free(my_array);
    return 0;
}
  • 58
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

游码客

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

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

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

打赏作者

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

抵扣说明:

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

余额充值