动态内存分配(malloc,calloc,realloc)和柔性数组
发布日期:2021-05-19 16:31:18 浏览次数:21 分类:精选文章

本文共 3143 字,大约阅读时间需要 10 分钟。

在声明数组时,必须用一个编译时常量指定数组的长度,但由于数组所需的内存空间取决于输入数据的大小,这在某些情况下是不确定的。例如,一个用于计算学生等级和平均分的程序可能需要存储一个班级所有学生的数据,但不同班级的学生数量可能有所不同。在这种情况下,使用动态内存分配是一种更好的选择。

首先,malloc 函数用于向内存申请一块连续可用的空间,并返回指向这块空间的指针。其函数原型为:

void* malloc (size_t size);

malloc 函数的使用注意事项如下:

  • 如果开辟成功,返回一个指向开辟成功空间的非空指针。
  • 如果开辟失败,返回一个空指针(即 NULL),因此在调用 malloc 后必须进行检查。
  • 返回值的类型为 void*,malloc 函数并不了解空间的具体类型,这需要在使用时由开发者自行决定。
  • 如果参数 size 为 0,malloc 的行为在编译器间可能存在差异,通常的情况下,这种行为是未定义的。
  • 接着,free 函数用于释放动态分配的内存。它的函数原型为:

    void free (void* ptr);

    free 函数的使用注意事项:

  • 它会释放 ptr 指针所指向的空间,释放之后 ptr 仍然指向释放的空间,因此在释放后建议将 ptr 设为 NULL。
  • 如果 ptr 不指向动态分配的内存,free 的行为是未定义的。
  • 如果 ptr 是一个空指针(即 NULL),函数将无需进行任何操作。
  • 以下是一个使用 mallocfree 的示例代码:

    #include 
    #include
    int main() {
    int* ptr = NULL;
    ptr = (int*)malloc(num * sizeof(int)); // 确保 num 是一个正整数
    if (ptr != NULL) {
    int i = 0;
    for (i = 0; i < num; i++) {
    *(ptr + i) = 0;
    }
    }
    free(ptr);
    ptr = NULL;
    return 0;
    }

    接下来,calloc 函数也用于分配内存,其函数原型为:

    void* calloc(size_t num_elements, size_t element_size);

    calloc 函数的主要特点是,它们不仅为 num_elements 个元素分配内存,还将每个元素的内容初始化为 0。它与 malloc 之间的主要区别在于前者会在返回指针之前将空间初始化为 0。一种常见使用方式是:

    #include 
    #include
    int main() {
    int* p = (int*)calloc(10, sizeof(int));
    if (p != NULL) {
    // 使用空间
    }
    free(p);
    p = NULL;
    }

    realloc 函数用于修改已分配的内存块的大小,其函数原型为:

    void* realloc(void* ptr, size_t new_size);

    realloc 函数的主要使用场景包括:

  • 将动态内存块扩大时,如果原有空间后面有足够的空间,新内存不会进行初始化。
  • 将动态内存块缩小时,会将原有空间重复复制到新分配的空间中并释放原有空间。
  • realloc 的使用也需要注意以下几点:

  • 如果 ptr 为 NULL,realloc 的行为与 malloc 相同,分配一块大小为 new_size 的空间。
  • 如果原有空间后面没有足够的空间,realloc 会将内容复制到新分配的空间中,原空间会被释放。此时,内容的完整性需要由开发者自行保证。
  • 以下是一个使用 realloc 的示例代码:

    #include 
    #include
    int main() {
    int* p = NULL;
    p = (int*)malloc(100);
    int* ptr = (int*)realloc(p, 1000);
    if (ptr != NULL) {
    p = ptr;
    }
    // 业务处理
    free(p);
    }

    在实际开发过程中,动态内存分配容易出现一些常见错误,开发者需要特别注意以下几点:

  • 对 NULL 解引用操作:必须确保 ptr 在使用 malloccalloc 之后不是 NULL。
  • 对动态内存空间的越界访问:必须严格控制访问范围,避免不小心或故意地超出动态内存空间。
  • 对非动态分配的内存进行释放:这可能导致运行时错误或内存泄漏,必须确保只对动态分配的内存使用 free
  • 释放一部分内存:这是不允许的,free 只能用于释放完整的动态内存块。
  • 多次释放同一块内存:会导致错误,必须避免。
  • 忘记释放动态内存:这可能导致内存泄漏,必须对所有动态分配的内存进行 free
  • 最后,关于柔性数组的使用,C99标准中允许在结构体中使用一个未知大小的数组成员(即柔性数组成员)。这种成员的组织方式有如下特点:

  • 燃性数组成员必须在结构体中至少有一个固定大小的成员 preceding。
  • sizeof 操作符返回的结构体大小不包括柔性数组占用的空间。
  • 包含柔性数组成员的结构体必须通过 malloc 函数进行动态内存分配,并且分配的空间大小必须大于结构体的大小,以便柔性数组成员有足够的空间进行保存。
  • 以下是一个柔性数组的示例代码:

    #include 
    #include
    typedef struct FlexArray {
    int n;
    char c;
    int arr[];
    } FlexArray;
    int main() {
    FlexArray* pF = (FlexArray*)malloc(sizeof(FlexArray) + 10 * sizeof(int));
    pF->n = 100;
    int i = 0;
    for (i = 0; i < 10; i++) {
    pF->arr[i] = i;
    }
    free(pF);
    pF = NULL;
    }

    在实际开发中,可以选择使用柔性数组成员来灵活存储未知长度的数据,或者手动分配内存,像这样:

    #include 
    #include
    typedef struct S {
    int n;
    int* arr;
    } s;
    int main() {
    s* ps = (s*)malloc(sizeof(s));
    ps->arr = (int*)malloc(sizeof(int) * 10);
    int i = 0;
    for (i = 0; i < 10; i++) {
    ps->arr[i] = i;
    }
    free(ps->arr);
    ps->arr = NULL;
    free(ps);
    ps = NULL;
    }

    两种方式的功能是相同的,但使用柔性数组的方式具有以下优势:

  • 方便内存的释放。
  • 具有较高的访问速度和缓存效率。
  • 上一篇:文件操作及常见文件操作函数使用
    下一篇:位段 + 枚举 + 联合体讲解

    发表评论

    最新留言

    哈哈,博客排版真的漂亮呢~
    [***.90.31.176]2025年04月24日 16时31分38秒