「C系列」C 内存管理

文章目录

  • [一、C 内存管理](#一、C 内存管理)
    • [1. 静态内存分配](#1. 静态内存分配)
    • [2. 栈内存分配](#2. 栈内存分配)
    • [3. 堆内存分配](#3. 堆内存分配)
    • 注意事项
  • [二、C 内存管理的函数和描述](#二、C 内存管理的函数和描述)
    • [1. `malloc()`](#1. malloc())
    • [2. `calloc()`](#2. calloc())
    • [3. `realloc()`](#3. realloc())
    • [4. `free()`](#4. free())
  • 三、相关链接

一、C 内存管理

C 语言的内存管理主要由程序员负责,这意味着你需要手动分配和释放内存以避免内存泄漏和其他相关问题。C 语言提供了几种用于内存管理的函数和操作符。

1. 静态内存分配

静态内存分配在编译时完成,通常用于定义全局变量、静态变量和数组。这些变量的生命周期是程序执行期间。

c 复制代码
int global_var;  // 全局变量,静态分配
static int static_var;  // 静态变量,也是静态分配
int array[10];  // 数组,静态分配

2. 栈内存分配

局部变量(包括函数内的非静态变量)通常在栈上分配。它们的生命周期是它们所在的函数或代码块的执行期间。栈内存由编译器自动管理,无需程序员手动分配和释放。

c 复制代码
void function() {
    int local_var;  // 局部变量,在栈上分配
    // ...
}

3. 堆内存分配

堆内存用于动态内存分配,这意味着你可以在运行时根据需要分配和释放内存。C 语言提供了几个函数来管理堆内存:

  • malloc():分配指定字节数的内存,并返回一个指向该内存的指针。如果分配失败,则返回 NULL
  • calloc():类似于 malloc(),但会初始化分配的内存为零。它还需要两个参数:要分配的元素的数量和每个元素的大小。
  • realloc():更改先前分配的内存块的大小。它可以扩大或缩小内存块。如果无法重新分配内存,则返回 NULL,并且原始内存块保持不变。
  • free():释放先前通过 malloc()calloc()realloc() 分配的内存。
c 复制代码
#include <stdlib.h>

int main() {
    int *dynamic_array;

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

    // 使用内存
    for (int i = 0; i < 10; i++) {
        dynamic_array[i] = i;
    }

    // ...

    // 释放内存
    free(dynamic_array);
    dynamic_array = NULL;  // 将指针设置为 NULL 是一个好习惯,可以防止悬挂指针

    return 0;
}

注意事项

  • 在使用动态内存分配时,务必检查 malloc()calloc()realloc() 的返回值,以确保它们没有返回 NULL
  • 使用 free() 释放内存后,务必将指针设置为 NULL,以防止悬挂指针(即指向已释放内存的指针)。
  • 避免在函数返回时返回局部变量的地址,因为局部变量在函数返回时会被销毁,其内存可能会被其他变量覆盖。
  • 谨慎使用 realloc(),因为它可能会移动内存块。如果 realloc() 无法在原地扩大内存块,它会分配一个新的内存块,并将原始内存块的内容复制到新位置,然后释放原始内存块。这意味着指向原始内存块的指针在 realloc() 调用后可能不再有效。

二、C 内存管理的函数和描述

在C语言中,与内存管理相关的几个主要函数包括 malloc(), calloc(), realloc(), 和 free()。下面我将为这些函数提供详细的描述和案例代码。

1. malloc()

函数描述
malloc() 函数用于动态分配指定字节大小的内存块,并返回一个指向该内存块的指针。如果内存分配失败,则返回 NULL

函数原型

c 复制代码
void *malloc(size_t size);

案例代码

c 复制代码
#include <stdio.h>
#include <stdlib.h>

int main() {
    int *ptr = malloc(10 * sizeof(int)); // 分配10个整数的内存
    if (ptr == NULL) {
        printf("Memory not allocated.\n");
        exit(0);
    }

    // 初始化数组
    for (int i = 0; i < 10; i++) {
        ptr[i] = i;
    }

    // 打印数组
    for (int i = 0; i < 10; i++) {
        printf("%d ", ptr[i]);
    }

    // 释放内存
    free(ptr);
    ptr = NULL;

    return 0;
}

2. calloc()

函数描述
calloc() 函数也用于动态分配内存,但与 malloc() 不同的是,它分配的内存会被初始化为零,并且你需要指定元素的数量和每个元素的大小,而不是总的字节数。

函数原型

c 复制代码
void *calloc(size_t num, size_t size);

案例代码

c 复制代码
#include <stdio.h>
#include <stdlib.h>

int main() {
    int *ptr = calloc(10, sizeof(int)); // 分配10个整数的内存,并初始化为0
    if (ptr == NULL) {
        printf("Memory not allocated.\n");
        exit(0);
    }

    // 打印数组(应为全0)
    for (int i = 0; i < 10; i++) {
        printf("%d ", ptr[i]);
    }

    // 释放内存
    free(ptr);
    ptr = NULL;

    return 0;
}

3. realloc()

函数描述
realloc() 函数用于改变已分配内存块的大小。它尝试在内存中为对象重新分配内存空间,并返回指向新内存块的指针。如果当前内存块足够大,则 realloc() 可能什么也不做并返回原始指针。如果当前内存块不够大,realloc() 会尝试找到一个足够大的内存块,并将原始数据复制到新的内存块中,然后返回新的内存块的指针。如果内存分配失败,则返回 NULL,并且原始内存块保持不变。

函数原型

c 复制代码
void *realloc(void *ptr, size_t size);

案例代码

c 复制代码
#include <stdio.h>
#include <stdlib.h>

int main() {
    int *ptr = malloc(5 * sizeof(int)); // 初始分配5个整数的内存
    if (ptr == NULL) {
        printf("Memory not allocated.\n");
        exit(0);
    }

    // 初始化数组
    for (int i = 0; i < 5; i++) {
        ptr[i] = i;
    }

    // 重新分配内存到10个整数
    ptr = realloc(ptr, 10 * sizeof(int));
    if (ptr == NULL) {
        printf("Memory reallocation failed.\n");
        free(ptr); // 尽管失败,但也要尝试释放原始内存
        exit(0);
    }

    // 初始化新分配的内存部分
    for (int i = 5; i < 10; i++) {
        ptr[i] = i;
    }

    // 打印数组
    for (int i = 0; i < 10; i++) {
        printf("%d ", ptr[i]);
    }

    // 释放内存
    free(ptr);
    ptr = NULL;

    return 0;
}

4. free()

在C语言中,free() 函数是用于释放之前通过 malloc(), calloc(), 或 realloc() 分配的内存块的。当你不再需要一块动态分配的内存时,应该使用 free() 函数来释放它,以防止内存泄漏。

函数原型

c 复制代码
void free(void *ptr);

参数

  • ptr:指向要释放的内存块的指针。这个指针必须是通过 malloc(), calloc(), 或 realloc() 分配得到的,且尚未被 free() 释放。

返回值

free() 函数没有返回值。

注意事项

  1. 不要重复释放 :如果你尝试使用 free() 释放同一块内存两次,或者释放一个已经被释放的内存块,程序可能会崩溃或产生不可预测的行为。

  2. 指针置空 :在调用 free() 后,最好将指针设置为 NULL。这可以防止对已释放内存的误用(悬挂指针)。

  3. 内存泄漏:如果你忘记释放分配的内存,就会导致内存泄漏。长时间运行的程序如果发生内存泄漏,可能会导致系统资源耗尽。

  4. 内存碎片化:频繁地分配和释放小块内存可能会导致内存碎片化,使得虽然总内存量充足,但无法找到足够大的连续内存块来满足新的分配请求。

案例代码

c 复制代码
#include <stdio.h>
#include <stdlib.h>

int main() {
    int *ptr = malloc(10 * sizeof(int)); // 分配10个整数的内存
    if (ptr == NULL) {
        printf("Memory not allocated.\n");
        return 1; // 分配失败,返回非零值
    }

    // 使用内存...

    // 释放内存
    free(ptr);
    ptr = NULL; // 防止悬挂指针

    return 0; // 成功执行
}

在这个例子中,我们首先使用 malloc() 分配了10个整数的内存,并检查是否分配成功。如果分配成功,我们就使用这块内存(虽然在这个简单的例子中我们并没有真正使用它)。然后,我们使用 free() 释放了这块内存,并将指针设置为 NULL,以防止悬挂指针。最后,程序成功执行并返回0。

三、相关链接

  1. Visual Studio Code下载地址
  2. Sublime Text下载地址
  3. 「C系列」C 简介
  4. 「C系列」C 基本语法
  5. 「C系列」C 数据类型
  6. 「C系列」C 变量及常见问题梳理
  7. 「C系列」C 常量
  8. 「C系列」C 存储类
  9. 「C系列」C 运算符
  10. 「C系列」C 判断/循环
  11. 「C系列」C 函数
  12. 「C系列」C 作用域规则
  13. 「C系列」C 数组
  14. 「C系列」C enum(枚举)
  15. 「C系列」C 指针及其应用案例
相关推荐
FeboReigns几秒前
C++简明教程(4)(Hello World)
c语言·c++
FeboReigns1 分钟前
C++简明教程(10)(初识类)
c语言·开发语言·c++
千天夜8 分钟前
多源多点路径规划:基于启发式动态生成树算法的实现
算法·机器学习·动态规划
从以前14 分钟前
准备考试:解决大学入学考试问题
数据结构·python·算法
向阳121831 分钟前
mybatis 缓存
java·缓存·mybatis
.Vcoistnt37 分钟前
Codeforces Round 994 (Div. 2)(A-D)
数据结构·c++·算法·贪心算法·动态规划
上等猿37 分钟前
函数式编程&Lambda表达式
java
蓝染-惣右介1 小时前
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
java·设计模式
小猿_001 小时前
C语言实现顺序表详解
c语言·开发语言
ALISHENGYA1 小时前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(实战训练三)
数据结构·c++·算法·图论