C語(yǔ)言學(xué)習(xí)6-動(dòng)態(tài)內(nèi)存管理指南 malloc & free

C語(yǔ)言學(xué)習(xí)1-數(shù)組、字符串
C語(yǔ)言學(xué)習(xí)2-指針的使用、const 指針
C語(yǔ)言學(xué)習(xí)3-指針和數(shù)組
C語(yǔ)言學(xué)習(xí)4-結(jié)構(gòu)體
C語(yǔ)言學(xué)習(xí)5-函數(shù)

一、為什么需要?jiǎng)討B(tài)內(nèi)存分配?

1.1 靜態(tài)數(shù)組的局限性

#include <stdio.h>

// 問(wèn)題:數(shù)組大小必須在編譯時(shí)確定
void static_array_limitations() {
    // ? 可以:編譯時(shí)常量
    int fixed_array[100];  // 固定100個(gè)元素
    
    // ? 錯(cuò)誤:運(yùn)行時(shí)變量作為數(shù)組大小
    int n;
    printf("請(qǐng)輸入需要的數(shù)組大小: ");
    scanf("%d", &n);
    // int dynamic_array[n];  // 編譯錯(cuò)誤!C89標(biāo)準(zhǔn)不支持
}

1.2 動(dòng)態(tài)內(nèi)存分配的優(yōu)勢(shì)

  • 按需分配:程序運(yùn)行時(shí)決定需要多少內(nèi)存
  • 靈活調(diào)整:可以隨時(shí)申請(qǐng)和釋放
  • 避免浪費(fèi):不預(yù)先分配過(guò)多內(nèi)存

二、malloc 和 free 基礎(chǔ)

2.1 內(nèi)存管理器(MM)概念

系統(tǒng)中有一個(gè)內(nèi)存管理器負(fù)責(zé)管理閑置內(nèi)存:

  • 應(yīng)用程序通過(guò) malloc 借出內(nèi)存
  • 使用完后通過(guò) free 歸還內(nèi)存
  • 所有程序共享同一個(gè)內(nèi)存池(堆)

2.2 malloc 函數(shù)詳解

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

void malloc_basic_usage() {
    // 申請(qǐng) 1024 字節(jié)內(nèi)存
    void* raw_ptr = malloc(1024);
    
    // 轉(zhuǎn)換為具體類型
    char* char_ptr = (char*)malloc(100);        // 100字節(jié)字符數(shù)組
    int* int_ptr = (int*)malloc(10 * sizeof(int));  // 10個(gè)整數(shù)的數(shù)組
    double* double_ptr = (double*)malloc(5 * sizeof(double)); // 5個(gè)雙精度數(shù)組
    
    printf("字符指針: %p\n", (void*)char_ptr);
    printf("整型指針: %p\n", (void*)int_ptr);
    printf("雙精度指針: %p\n", (void*)double_ptr);
    
    // 記得釋放!
    free(raw_ptr);
    free(char_ptr);
    free(int_ptr);
    free(double_ptr);
}

2.3 free 函數(shù)詳解

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

struct Contact {
    int id;
    char name[32];
    char phone[16];
};

void free_basic_usage() {
    // 申請(qǐng)內(nèi)存
    int size = 5 * sizeof(struct Contact);
    struct Contact* contacts = (struct Contact*)malloc(size);
    
    if (contacts != NULL) {
        // 使用內(nèi)存
        contacts[0].id = 1;
        strcpy(contacts[0].name, "張三");
        strcpy(contacts[0].phone, "13800138000");
        
        printf("聯(lián)系人: %s, 電話: %s\n", contacts[0].name, contacts[0].phone);
        
        // 釋放內(nèi)存
        free(contacts);
        contacts = NULL;  // 良好習(xí)慣:釋放后置為NULL
    }
}

三、動(dòng)態(tài)內(nèi)存的實(shí)際應(yīng)用

3.1 用戶自定義大小的數(shù)組

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

void dynamic_array_example() {
    int count;
    
    printf("請(qǐng)輸入需要存儲(chǔ)的聯(lián)系人數(shù)量: ");
    scanf("%d", &count);
    
    // 動(dòng)態(tài)分配足夠的內(nèi)存
    struct Contact* contacts = (struct Contact*)malloc(count * sizeof(struct Contact));
    
    if (contacts == NULL) {
        printf("錯(cuò)誤:內(nèi)存分配失?。n");
        return;
    }
    
    // 輸入數(shù)據(jù)
    for (int i = 0; i < count; i++) {
        printf("\n請(qǐng)輸入第 %d 個(gè)聯(lián)系人的信息:\n", i + 1);
        contacts[i].id = i + 1;
        
        printf("姓名: ");
        scanf("%s", contacts[i].name);
        
        printf("電話: ");
        scanf("%s", contacts[i].phone);
    }
    
    // 顯示數(shù)據(jù)
    printf("\n=== 所有聯(lián)系人 ===\n");
    for (int i = 0; i < count; i++) {
        printf("%d. %s - %s\n", contacts[i].id, contacts[i].name, contacts[i].phone);
    }
    
    // 釋放內(nèi)存
    free(contacts);
    contacts = NULL;
}

3.2 指針數(shù)組的動(dòng)態(tài)分配

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

void pointer_array_example() {
    int array_size = 5;
    
    // 分配指針數(shù)組:包含5個(gè)double指針
    double** pointer_array = (double**)malloc(array_size * sizeof(double*));
    
    if (pointer_array == NULL) {
        printf("指針數(shù)組分配失?。n");
        return;
    }
    
    // 為每個(gè)指針?lè)峙鋬?nèi)存
    for (int i = 0; i < array_size; i++) {
        pointer_array[i] = (double*)malloc(sizeof(double));
        
        if (pointer_array[i] == NULL) {
            printf("元素 %d 分配失??!\n", i);
            // 釋放已分配的內(nèi)存
            for (int j = 0; j < i; j++) {
                free(pointer_array[j]);
            }
            free(pointer_array);
            return;
        }
        
        *pointer_array[i] = i * 1.5;  // 賦值
    }
    
    // 使用數(shù)據(jù)
    printf("指針數(shù)組內(nèi)容:\n");
    for (int i = 0; i < array_size; i++) {
        printf("pointer_array[%d] = %.2f\n", i, *pointer_array[i]);
    }
    
    // 正確釋放:先釋放元素,再釋放數(shù)組
    for (int i = 0; i < array_size; i++) {
        free(pointer_array[i]);
        pointer_array[i] = NULL;
    }
    free(pointer_array);
    pointer_array = NULL;
}

3.3 跨函數(shù)使用動(dòng)態(tài)內(nèi)存

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

// 修改動(dòng)態(tài)內(nèi)存內(nèi)容的函數(shù)
void modify_dynamic_memory(int* ptr, int new_value) {
    if (ptr != NULL) {
        *ptr = new_value;
    }
}

// 創(chuàng)建動(dòng)態(tài)內(nèi)存的函數(shù)
int* create_dynamic_integer(int initial_value) {
    int* ptr = (int*)malloc(sizeof(int));
    if (ptr != NULL) {
        *ptr = initial_value;
    }
    return ptr;
}

// 釋放動(dòng)態(tài)內(nèi)存的函數(shù)
void destroy_dynamic_integer(int* ptr) {
    if (ptr != NULL) {
        free(ptr);
        // 注意:這里不能將外部指針置為NULL,調(diào)用者需要自己處理
    }
}

void cross_function_example() {
    // 在一個(gè)函數(shù)中創(chuàng)建
    int* number = create_dynamic_integer(42);
    
    if (number != NULL) {
        printf("初始值: %d\n", *number);  // 輸出: 42
        
        // 在另一個(gè)函數(shù)中修改
        modify_dynamic_memory(number, 100);
        printf("修改后: %d\n", *number);  // 輸出: 100
        
        // 在另一個(gè)函數(shù)中釋放
        destroy_dynamic_integer(number);
        number = NULL;  // 調(diào)用者負(fù)責(zé)置為NULL
    }
}

四、對(duì)象與動(dòng)態(tài)內(nèi)存

4.1 對(duì)象的概念

在C語(yǔ)言中,對(duì)象指的是一塊內(nèi)存區(qū)域,用于存儲(chǔ)數(shù)據(jù)。

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

// 汽車結(jié)構(gòu)體
struct Car {
    char maker[32];
    int price;
};

// 市民結(jié)構(gòu)體
struct Citizen {
    char name[32];
    int deposit;
    struct Car* car;  // 指向Car對(duì)象的指針
};

// 購(gòu)買汽車
void buy_car(struct Citizen* owner) {
    if (owner == NULL) return;
    
    // 動(dòng)態(tài)創(chuàng)建Car對(duì)象
    struct Car* new_car = (struct Car*)malloc(sizeof(struct Car));
    if (new_car == NULL) {
        printf("購(gòu)車失?。簝?nèi)存不足!\n");
        return;
    }
    
    strcpy(new_car->maker, "雪佛蘭");
    new_car->price = 100000;
    
    if (owner->deposit >= new_car->price) {
        owner->car = new_car;
        owner->deposit -= new_car->price;
        printf("%s 購(gòu)買了 %s,花費(fèi) %d 元\n", owner->name, new_car->maker, new_car->price);
    } else {
        printf("%s 存款不足,無(wú)法購(gòu)買!\n", owner->name);
        free(new_car);  // 立即釋放
    }
}

// 報(bào)廢汽車
void discard_car(struct Citizen* owner) {
    if (owner != NULL && owner->car != NULL) {
        printf("%s 報(bào)廢了 %s\n", owner->name, owner->car->maker);
        free(owner->car);
        owner->car = NULL;
    }
}

// 轉(zhuǎn)讓汽車
void transfer_car(struct Citizen* from, struct Citizen* to) {
    if (from == NULL || to == NULL || from->car == NULL) return;
    
    printf("%s 將 %s 轉(zhuǎn)讓給 %s\n", from->name, from->car->maker, to->name);
    to->car = from->car;
    from->car = NULL;  // 原主人不再擁有汽車
}

void object_management_example() {
    // 創(chuàng)建市民對(duì)象(棧上)
    struct Citizen alice = {"Alice", 200000, NULL};
    struct Citizen bob = {"Bob", 50000, NULL};
    
    // Alice 購(gòu)買汽車
    buy_car(&alice);
    printf("Alice 剩余存款: %d\n", alice.deposit);
    
    // 轉(zhuǎn)讓給 Bob
    transfer_car(&alice, &bob);
    
    // Bob 報(bào)廢汽車
    discard_car(&bob);
}

五、malloc 和 free 的注意事項(xiàng)

5.1 必須遵守的規(guī)則

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

void malloc_free_rules() {
    // ? 規(guī)則1:只能free通過(guò)malloc分配的內(nèi)存
    int* valid_ptr = (int*)malloc(sizeof(int));
    free(valid_ptr);  // 正確
    
    // ? 錯(cuò)誤:free非malloc的指針
    int stack_variable = 10;
    int* stack_ptr = &stack_variable;
    // free(stack_ptr);  // 運(yùn)行時(shí)錯(cuò)誤!
    
    // ? 規(guī)則2:必須free首地址
    char* full_block = (char*)malloc(100);
    free(full_block);  // 正確:釋放整個(gè)塊
    // free(full_block + 50);  // 錯(cuò)誤:不是首地址
    
    // ? 規(guī)則3:free后立即置為NULL
    int* ptr = (int*)malloc(sizeof(int));
    free(ptr);
    ptr = NULL;  // 防止懸空指針
    
    // ? 錯(cuò)誤:使用已free的內(nèi)存
    // *ptr = 100;  // 未定義行為!
}

5.2 內(nèi)存泄漏檢測(cè)

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

void memory_leak_example() {
    // ? 內(nèi)存泄漏:分配后沒(méi)有釋放
    for (int i = 0; i < 10; i++) {
        int* leak = (int*)malloc(sizeof(int));
        *leak = i;
        // 忘記 free(leak)!
    }
    
    // ? 正確:及時(shí)釋放
    for (int i = 0; i < 10; i++) {
        int* proper = (int*)malloc(sizeof(int));
        if (proper != NULL) {
            *proper = i;
            // 使用...
            free(proper);  // 及時(shí)釋放
            proper = NULL;
        }
    }
}

void infinite_memory_leak() {
    // ? 危險(xiǎn):無(wú)限循環(huán)分配內(nèi)存
    /*
    while (1) {
        void* memory = malloc(1024 * 1024);  // 每次1MB
        if (memory == NULL) {
            printf("內(nèi)存耗盡!\n");
            break;
        }
        // 沒(méi)有free!
    }
    */
}

5.3 返回值檢查

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

void safe_malloc_usage() {
    // 申請(qǐng)大量?jī)?nèi)存
    void* large_block = malloc(1024 * 1024 * 1024);  // 1GB
    
    if (large_block == NULL) {
        printf("警告:內(nèi)存分配失?。∠到y(tǒng)可能內(nèi)存不足。\n");
        // 嘗試分配較小的內(nèi)存
        large_block = malloc(1024 * 1024);  // 1MB
    }
    
    if (large_block != NULL) {
        printf("內(nèi)存分配成功!\n");
        // 使用內(nèi)存...
        free(large_block);
    } else {
        printf("所有內(nèi)存分配嘗試都失敗了!\n");
    }
}

六、內(nèi)存操作函數(shù)

6.1 memset - 內(nèi)存設(shè)置

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

void memset_example() {
    // 分配并初始化內(nèi)存
    int* numbers = (int*)malloc(10 * sizeof(int));
    
    if (numbers != NULL) {
        // 將內(nèi)存塊全部設(shè)置為0
        memset(numbers, 0, 10 * sizeof(int));
        
        // 驗(yàn)證初始化結(jié)果
        for (int i = 0; i < 10; i++) {
            printf("numbers[%d] = %d\n", i, numbers[i]);  // 全部為0
        }
        
        // 設(shè)置為特定值(注意:對(duì)于int類型要小心)
        memset(numbers, 0xFF, 10 * sizeof(int));  // 全部設(shè)置為-1
        
        free(numbers);
    }
    
    // 字符數(shù)組的memset使用
    char* text = (char*)malloc(100);
    if (text != NULL) {
        memset(text, 'A', 99);    // 前99字節(jié)設(shè)置為'A'
        text[99] = '\0';          // 最后1字節(jié)設(shè)置為字符串結(jié)束符
        printf("文本: %s\n", text);
        free(text);
    }
}

6.2 memcpy - 內(nèi)存拷貝

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

void memcpy_example() {
    int source[5] = {1, 2, 3, 4, 5};
    int* destination = (int*)malloc(5 * sizeof(int));
    
    if (destination != NULL) {
        // 拷貝整個(gè)數(shù)組
        memcpy(destination, source, 5 * sizeof(int));
        
        printf("拷貝結(jié)果:\n");
        for (int i = 0; i < 5; i++) {
            printf("dest[%d] = %d\n", i, destination[i]);
        }
        
        free(destination);
    }
    
    // 結(jié)構(gòu)體拷貝
    struct Point {
        int x, y;
    };
    
    struct Point p1 = {10, 20};
    struct Point* p2 = (struct Point*)malloc(sizeof(struct Point));
    
    if (p2 != NULL) {
        memcpy(p2, &p1, sizeof(struct Point));
        printf("點(diǎn)坐標(biāo): (%d, %d)\n", p2->x, p2->y);
        free(p2);
    }
}

6.3 memmove - 安全內(nèi)存移動(dòng)

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

void memmove_example() {
    char buffer[] = "Hello, World!";
    
    printf("移動(dòng)前: %s\n", buffer);
    
    // 重疊內(nèi)存區(qū)域的移動(dòng)
    // 將 "Hello" 移動(dòng)到 "World" 的位置
    memmove(buffer + 7, buffer, 5);
    
    printf("移動(dòng)后: %s\n", buffer);  // 輸出: Hello, Hello!
    
    // 與memcpy的對(duì)比
    char data1[] = "ABCDEFGHIJ";
    char data2[] = "ABCDEFGHIJ";
    
    // memcpy在重疊時(shí)可能出錯(cuò)
    // memcpy(data1 + 2, data1, 5);  // 未定義行為
    
    // memmove總是安全的
    memmove(data2 + 2, data2, 5);
    printf("memmove結(jié)果: %s\n", data2);
}

七、最佳實(shí)踐總結(jié)

7.1 動(dòng)態(tài)內(nèi)存使用原則

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

// 模板:安全的動(dòng)態(tài)內(nèi)存使用模式
void safe_dynamic_memory_pattern() {
    // 1. 聲明指針并初始化為NULL
    int* data = NULL;
    size_t element_count = 100;
    
    // 2. 分配內(nèi)存并檢查成功
    data = (int*)malloc(element_count * sizeof(int));
    if (data == NULL) {
        printf("錯(cuò)誤:內(nèi)存分配失敗\n");
        return;  // 早期返回
    }
    
    // 3. 使用內(nèi)存前可以初始化
    memset(data, 0, element_count * sizeof(int));
    
    // 4. 安全使用內(nèi)存
    for (size_t i = 0; i < element_count; i++) {
        data[i] = (int)(i * i);
    }
    
    // 5. 使用完畢后立即釋放
    free(data);
    data = NULL;  // 防止懸空指針
    
    printf("動(dòng)態(tài)內(nèi)存使用完成\n");
}

7.2 錯(cuò)誤處理模式

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

// 帶完整錯(cuò)誤處理的動(dòng)態(tài)數(shù)組
int* create_dynamic_array(size_t size, int initial_value) {
    if (size == 0) {
        printf("錯(cuò)誤:數(shù)組大小不能為0\n");
        return NULL;
    }
    
    int* array = (int*)malloc(size * sizeof(int));
    if (array == NULL) {
        printf("錯(cuò)誤:無(wú)法分配 %zu 個(gè)整數(shù)的內(nèi)存\n", size);
        return NULL;
    }
    
    // 初始化數(shù)組
    for (size_t i = 0; i < size; i++) {
        array[i] = initial_value;
    }
    
    return array;
}

void cleanup_dynamic_array(int* array) {
    if (array != NULL) {
        free(array);
        // 注意:這里不能修改外部指針,調(diào)用者需要自己置NULL
    }
}

void demonstrate_best_practices() {
    int* numbers = NULL;
    
    numbers = create_dynamic_array(100, 0);
    if (numbers == NULL) {
        return;  // 分配失敗,直接返回
    }
    
    // 使用數(shù)組...
    for (int i = 0; i < 100; i++) {
        numbers[i] = i * 2;
    }
    
    // 清理
    cleanup_dynamic_array(numbers);
    numbers = NULL;  // 調(diào)用者負(fù)責(zé)置NULL
}

7.3 核心要點(diǎn)回顧

  1. 分配前:確定需要的大小,檢查參數(shù)有效性
  2. 分配時(shí):檢查malloc返回值,處理分配失敗
  3. 使用時(shí):確保不越界訪問(wèn),及時(shí)處理錯(cuò)誤
  4. 釋放時(shí):只free malloc返回的地址,釋放后置NULL
  5. 設(shè)計(jì)時(shí):誰(shuí)分配誰(shuí)釋放,或明確所有權(quán)轉(zhuǎn)移

掌握這些動(dòng)態(tài)內(nèi)存管理技術(shù),你就能編寫出高效、安全的C語(yǔ)言程序!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容