动态内存分配

深入理解动态内存分配

动态内存分配是C语言编程的核心技能,它允许程序在运行时根据需要分配内存。东巴文(db-w.cn) 将带你深入理解动态内存分配的原理与实践。

💡 东巴文观点:动态内存分配赋予了程序灵活性,但也带来了内存管理的责任。正确的内存管理是程序稳定运行的基础。

为什么需要动态内存分配

静态分配的局限性

#include <stdio.h>

void staticAllocationLimitation() {
    printf("=== 东巴文静态分配的局限性 ===\n\n");
    
    // 静态分配:编译时确定大小
    int staticArray[100];  // 固定大小
    
    printf("静态分配的特点:\n");
    printf("  1. 编译时确定大小\n");
    printf("  2. 大小固定,不能改变\n");
    printf("  3. 可能造成内存浪费\n");
    printf("  4. 可能造成内存不足\n");
    
    printf("\n问题:\n");
    printf("  - 如果需要1000个元素?\n");
    printf("  - 如果只需要10个元素?\n");
    printf("  - 如果运行时才知道需要多少?\n");
}

int main() {
    staticAllocationLimitation();
    return 0;
}

动态分配的优势

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

void dynamicAllocationAdvantage() {
    printf("=== 东巴文动态分配的优势 ===\n\n");
    
    int size;
    printf("请输入数组大小:");
    scanf("%d", &size);
    
    // 动态分配:运行时确定大小
    int *dynamicArray = (int *)malloc(size * sizeof(int));
    
    if (dynamicArray != NULL) {
        printf("\n动态分配成功!\n");
        printf("  分配的元素个数:%d\n", size);
        printf("  分配的字节数:%zu\n", size * sizeof(int));
        
        // 使用数组...
        
        free(dynamicArray);
    } else {
        printf("内存分配失败!\n");
    }
}

int main() {
    dynamicAllocationAdvantage();
    return 0;
}

malloc函数

基本用法

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

int main() {
    printf("=== 东巴文malloc函数基本用法 ===\n\n");
    
    // 分配单个变量
    int *ptr = (int *)malloc(sizeof(int));
    
    if (ptr != NULL) {
        *ptr = 100;
        printf("分配的值:*ptr = %d\n", *ptr);
        printf("分配的地址:ptr = %p\n", ptr);
        
        free(ptr);
        ptr = NULL;
    } else {
        printf("内存分配失败!\n");
    }
    
    return 0;
}

东巴文说明

  • malloc 在堆区分配内存
  • 参数:需要分配的字节数
  • 返回值:void* 指针,失败返回 NULL
  • 分配的内存未初始化

分配数组

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

int main() {
    printf("=== 东巴文malloc分配数组 ===\n\n");
    
    int size = 5;
    int *arr = (int *)malloc(size * sizeof(int));
    
    if (arr != NULL) {
        // 初始化
        for (int i = 0; i < size; i++) {
            arr[i] = i + 1;
        }
        
        // 输出
        printf("数组内容:\n");
        for (int i = 0; i < size; i++) {
            printf("arr[%d] = %d\n", i, arr[i]);
        }
        
        free(arr);
        arr = NULL;
    }
    
    return 0;
}

分配结构体

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

typedef struct {
    char name[50];
    int age;
    float score;
} Student;

int main() {
    printf("=== 东巴文malloc分配结构体 ===\n\n");
    
    // 分配单个结构体
    Student *stu = (Student *)malloc(sizeof(Student));
    
    if (stu != NULL) {
        strcpy(stu->name, "东巴文");
        stu->age = 20;
        stu->score = 95.5;
        
        printf("学生信息:\n");
        printf("  姓名:%s\n", stu->name);
        printf("  年龄:%d\n", stu->age);
        printf("  成绩:%.1f\n", stu->score);
        
        free(stu);
        stu = NULL;
    }
    
    return 0;
}

calloc函数

基本用法

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

int main() {
    printf("=== 东巴文calloc函数基本用法 ===\n\n");
    
    int size = 5;
    int *arr = (int *)calloc(size, sizeof(int));
    
    if (arr != NULL) {
        printf("calloc分配的数组(已初始化为0):\n");
        for (int i = 0; i < size; i++) {
            printf("arr[%d] = %d\n", i, arr[i]);
        }
        
        free(arr);
        arr = NULL;
    }
    
    return 0;
}

东巴文说明

  • calloc 分配并初始化为0
  • 两个参数:元素个数和元素大小
  • 返回值:void* 指针,失败返回 NULL
  • 适合分配数组

malloc vs calloc

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

int main() {
    printf("=== 东巴文malloc vs calloc ===\n\n");
    
    int size = 5;
    
    // malloc:未初始化
    int *arr1 = (int *)malloc(size * sizeof(int));
    printf("malloc分配(未初始化):\n");
    for (int i = 0; i < size; i++) {
        printf("arr1[%d] = %d\n", i, arr1[i]);
    }
    
    // calloc:初始化为0
    int *arr2 = (int *)calloc(size, sizeof(int));
    printf("\ncalloc分配(初始化为0):\n");
    for (int i = 0; i < size; i++) {
        printf("arr2[%d] = %d\n", i, arr2[i]);
    }
    
    free(arr1);
    free(arr2);
    
    return 0;
}

东巴文对比表

特性 malloc calloc
参数 字节数 元素个数,元素大小
初始化 未初始化 初始化为0
效率 较快 较慢
适用场景 通用 数组、结构体数组

realloc函数

基本用法

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

int main() {
    printf("=== 东巴文realloc函数基本用法 ===\n\n");
    
    // 初始分配
    int size = 5;
    int *arr = (int *)malloc(size * sizeof(int));
    
    if (arr != NULL) {
        // 初始化
        for (int i = 0; i < size; i++) {
            arr[i] = i + 1;
        }
        
        printf("初始分配:\n");
        for (int i = 0; i < size; i++) {
            printf("arr[%d] = %d\n", i, arr[i]);
        }
        
        // 扩展内存
        int newSize = 10;
        int *newArr = (int *)realloc(arr, newSize * sizeof(int));
        
        if (newArr != NULL) {
            arr = newArr;
            
            printf("\n扩展后:\n");
            for (int i = 0; i < newSize; i++) {
                printf("arr[%d] = %d\n", i, arr[i]);
            }
        }
        
        free(arr);
        arr = NULL;
    }
    
    return 0;
}

东巴文说明

  • realloc 调整已分配内存的大小
  • 第一个参数:原指针
  • 第二个参数:新大小
  • 返回值:新指针,失败返回 NULL
  • 可能移动内存位置
  • 原有数据保持不变

realloc的特殊用法

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

int main() {
    printf("=== 东巴文realloc的特殊用法 ===\n\n");
    
    // realloc(NULL, size) 等价于 malloc(size)
    int *ptr1 = (int *)realloc(NULL, sizeof(int));
    *ptr1 = 100;
    printf("realloc(NULL, size):*ptr1 = %d\n", *ptr1);
    
    // realloc(ptr, 0) 等价于 free(ptr)
    int *ptr2 = (int *)malloc(sizeof(int));
    *ptr2 = 200;
    printf("realloc(ptr, 0):释放内存\n");
    realloc(ptr2, 0);
    
    free(ptr1);
    
    return 0;
}

free函数

基本用法

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

int main() {
    printf("=== 东巴文free函数基本用法 ===\n\n");
    
    int *ptr = (int *)malloc(sizeof(int));
    
    if (ptr != NULL) {
        *ptr = 100;
        printf("分配的值:*ptr = %d\n", *ptr);
        
        // 释放内存
        free(ptr);
        printf("内存已释放\n");
        
        // ✅ 推荐:释放后置NULL
        ptr = NULL;
    }
    
    return 0;
}

东巴文说明

  • free 释放动态分配的内存
  • 参数:要释放的指针
  • 释放后指针变为悬空指针
  • 建议释放后置 NULL
  • 不能重复释放
  • 释放 NULL 是安全的

常见错误

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

int main() {
    printf("=== 东巴文free的常见错误 ===\n\n");
    
    int *ptr = (int *)malloc(sizeof(int));
    *ptr = 100;
    
    // ❌ 错误1:重复释放
    free(ptr);
    // free(ptr);  // 错误:重复释放
    
    // ✅ 正确:释放后置NULL
    ptr = NULL;
    free(ptr);  // 释放NULL是安全的
    
    // ❌ 错误2:释放非动态分配的内存
    int localVar = 10;
    // free(&localVar);  // 错误:释放栈区内存
    
    // ❌ 错误3:释放部分内存
    int *arr = (int *)malloc(10 * sizeof(int));
    // free(arr + 5);  // 错误:释放部分内存
    free(arr);  // 正确:释放整个内存
    
    printf("正确使用free\n");
    
    return 0;
}

动态数组

一维动态数组

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

int main() {
    printf("=== 东巴文一维动态数组 ===\n\n");
    
    int size;
    printf("请输入数组大小:");
    scanf("%d", &size);
    
    int *arr = (int *)malloc(size * sizeof(int));
    
    if (arr != NULL) {
        // 初始化
        for (int i = 0; i < size; i++) {
            arr[i] = i + 1;
        }
        
        // 输出
        printf("数组内容:\n");
        for (int i = 0; i < size; i++) {
            printf("%d ", arr[i]);
        }
        printf("\n");
        
        free(arr);
    }
    
    return 0;
}

二维动态数组

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

int main() {
    printf("=== 东巴文二维动态数组 ===\n\n");
    
    int rows = 3, cols = 4;
    
    // 分配指针数组
    int **matrix = (int **)malloc(rows * sizeof(int *));
    
    if (matrix != NULL) {
        // 为每一行分配内存
        for (int i = 0; i < rows; i++) {
            matrix[i] = (int *)malloc(cols * sizeof(int));
        }
        
        // 初始化
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                matrix[i][j] = i * cols + j + 1;
            }
        }
        
        // 输出
        printf("二维数组:\n");
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                printf("%2d ", matrix[i][j]);
            }
            printf("\n");
        }
        
        // 释放内存
        for (int i = 0; i < rows; i++) {
            free(matrix[i]);
        }
        free(matrix);
    }
    
    return 0;
}

动态字符串数组

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

int main() {
    printf("=== 东巴文动态字符串数组 ===\n\n");
    
    int count = 3;
    
    // 分配指针数组
    char **strings = (char **)malloc(count * sizeof(char *));
    
    if (strings != NULL) {
        // 为每个字符串分配内存
        for (int i = 0; i < count; i++) {
            strings[i] = (char *)malloc(50 * sizeof(char));
        }
        
        // 初始化
        strcpy(strings[0], "Hello");
        strcpy(strings[1], "东巴文");
        strcpy(strings[2], "db-w.cn");
        
        // 输出
        printf("字符串数组:\n");
        for (int i = 0; i < count; i++) {
            printf("strings[%d] = %s\n", i, strings[i]);
        }
        
        // 释放内存
        for (int i = 0; i < count; i++) {
            free(strings[i]);
        }
        free(strings);
    }
    
    return 0;
}

东巴文最佳实践

1. 检查返回值

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

int main() {
    printf("=== 东巴文检查返回值 ===\n\n");
    
    // ✅ 正确:检查返回值
    int *ptr = (int *)malloc(sizeof(int));
    
    if (ptr != NULL) {
        *ptr = 100;
        printf("ptr = %d\n", *ptr);
        free(ptr);
        ptr = NULL;
    } else {
        printf("内存分配失败!\n");
    }
    
    return 0;
}

2. 及时释放内存

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

void processData() {
    int *data = (int *)malloc(100 * sizeof(int));
    
    if (data != NULL) {
        // 使用数据...
        
        // ✅ 正确:及时释放
        free(data);
        data = NULL;
    }
}

int main() {
    printf("=== 东巴文及时释放内存 ===\n\n");
    
    processData();
    
    printf("内存已正确释放\n");
    
    return 0;
}

3. 使用sizeof运算符

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

int main() {
    printf("=== 东巴文使用sizeof运算符 ===\n\n");
    
    // ✅ 正确:使用sizeof
    int *ptr1 = (int *)malloc(sizeof(int));
    int *ptr2 = (int *)malloc(10 * sizeof(int));
    
    // ❌ 错误:硬编码大小
    // int *ptr3 = (int *)malloc(4);  // 不同平台可能不同
    
    printf("使用sizeof确保可移植性\n");
    
    free(ptr1);
    free(ptr2);
    
    return 0;
}

4. 避免内存泄漏

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

void noMemoryLeak() {
    printf("=== 东巴文避免内存泄漏 ===\n\n");
    
    int *ptr = (int *)malloc(sizeof(int));
    
    if (ptr != NULL) {
        *ptr = 100;
        printf("ptr = %d\n", *ptr);
        
        // ✅ 正确:释放内存
        free(ptr);
        ptr = NULL;
    }
    
    printf("无内存泄漏\n");
}

int main() {
    noMemoryLeak();
    return 0;
}

东巴文验证清单

完成本章学习后,请确认:

  • 理解动态内存分配的必要性
  • 掌握malloc函数的使用
  • 掌握calloc函数的使用
  • 掌握realloc函数的使用
  • 掌握free函数的使用
  • 掌握动态数组的创建
  • 掌握最佳实践

下一步学习

掌握动态内存分配后,你可以继续学习:

如果遇到问题,欢迎访问 东巴文(db-w.cn) 获取帮助!


东巴文(db-w.cn) - 让编程学习更简单

🎯 东巴文动态内存分配提示:动态内存分配赋予了程序灵活性,但也带来了内存管理的责任。在 db-w.cn,我们会通过大量实例帮你掌握动态内存分配!