Skip to content

动态内存

概述

动态内存管理允许程序在运行时分配和释放内存,这对于处理不确定大小的数据结构非常重要。

内存区域

C程序的内存分为以下几个区域:

区域说明
栈(Stack)自动变量,函数调用信息
堆(Heap)动态分配的内存
全局/静态区全局变量,静态变量
常量区字符串常量,const变量
代码区程序指令

malloc函数

基本用法

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

int main() {
    int *ptr = (int*)malloc(sizeof(int));
    
    if (ptr == NULL) {
        printf("内存分配失败\n");
        return 1;
    }
    
    *ptr = 100;
    printf("值: %d\n", *ptr);
    
    free(ptr);
    
    return 0;
}

分配数组

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

int main() {
    int n = 5;
    int *arr = (int*)malloc(n * sizeof(int));
    
    if (arr == NULL) {
        printf("内存分配失败\n");
        return 1;
    }
    
    for (int i = 0; i < n; i++) {
        arr[i] = i * 10;
    }
    
    for (int i = 0; i < n; i++) {
        printf("arr[%d] = %d\n", i, arr[i]);
    }
    
    free(arr);
    
    return 0;
}

calloc函数

calloc会初始化内存为零。

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

int main() {
    int n = 5;
    int *arr = (int*)calloc(n, sizeof(int));
    
    if (arr == NULL) {
        printf("内存分配失败\n");
        return 1;
    }
    
    printf("calloc初始化后的值:\n");
    for (int i = 0; i < n; i++) {
        printf("arr[%d] = %d\n", i, arr[i]);
    }
    
    free(arr);
    
    return 0;
}

malloc与calloc的区别

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

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

realloc函数

重新调整已分配内存的大小。

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

int main() {
    int n = 5;
    int *arr = (int*)malloc(n * sizeof(int));
    
    if (arr == NULL) {
        printf("内存分配失败\n");
        return 1;
    }
    
    for (int i = 0; i < n; i++) {
        arr[i] = i + 1;
    }
    
    printf("原始数组:\n");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    
    int new_n = 10;
    int *temp = (int*)realloc(arr, new_n * sizeof(int));
    
    if (temp == NULL) {
        printf("内存重新分配失败\n");
        free(arr);
        return 1;
    }
    
    arr = temp;
    
    for (int i = n; i < new_n; i++) {
        arr[i] = i + 1;
    }
    
    printf("扩展后的数组:\n");
    for (int i = 0; i < new_n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    
    free(arr);
    
    return 0;
}

free函数

基本用法

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

int main() {
    int *ptr = (int*)malloc(sizeof(int));
    
    if (ptr != NULL) {
        *ptr = 100;
        printf("值: %d\n", *ptr);
        free(ptr);
        ptr = NULL;
    }
    
    return 0;
}

避免内存泄漏

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

void process() {
    int *data = (int*)malloc(100 * sizeof(int));
    
    if (data == NULL) return;
    
    for (int i = 0; i < 100; i++) {
        data[i] = i;
    }
    
    printf("处理完成\n");
    
    free(data);
    data = NULL;
}

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

常见问题

内存泄漏

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

void badExample() {
    int *ptr = (int*)malloc(sizeof(int));
    *ptr = 100;
    printf("值: %d\n", *ptr);
}

void goodExample() {
    int *ptr = (int*)malloc(sizeof(int));
    if (ptr != NULL) {
        *ptr = 100;
        printf("值: %d\n", *ptr);
        free(ptr);
    }
}

int main() {
    printf("错误示例(内存泄漏):\n");
    badExample();
    
    printf("\n正确示例:\n");
    goodExample();
    
    return 0;
}

重复释放

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

int main() {
    int *ptr = (int*)malloc(sizeof(int));
    
    if (ptr != NULL) {
        *ptr = 100;
        free(ptr);
        ptr = NULL;
    }
    
    if (ptr != NULL) {
        free(ptr);
    }
    
    printf("安全释放完成\n");
    
    return 0;
}

悬空指针

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

int main() {
    int *ptr = (int*)malloc(sizeof(int));
    
    if (ptr != NULL) {
        *ptr = 100;
        printf("值: %d\n", *ptr);
        
        free(ptr);
        ptr = NULL;
    }
    
    if (ptr == NULL) {
        printf("指针已置空,安全\n");
    }
    
    return 0;
}

越界访问

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

int main() {
    int n = 5;
    int *arr = (int*)malloc(n * sizeof(int));
    
    if (arr == NULL) {
        return 1;
    }
    
    for (int i = 0; i < n; i++) {
        arr[i] = i;
    }
    
    printf("正确访问:\n");
    for (int i = 0; i < n; i++) {
        printf("arr[%d] = %d\n", i, arr[i]);
    }
    
    free(arr);
    
    return 0;
}

动态数据结构

动态数组

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

typedef struct {
    int *data;
    int size;
    int capacity;
} DynamicArray;

DynamicArray* createArray(int initialCapacity) {
    DynamicArray *arr = (DynamicArray*)malloc(sizeof(DynamicArray));
    if (arr == NULL) return NULL;
    
    arr->data = (int*)malloc(initialCapacity * sizeof(int));
    if (arr->data == NULL) {
        free(arr);
        return NULL;
    }
    
    arr->size = 0;
    arr->capacity = initialCapacity;
    return arr;
}

void pushBack(DynamicArray *arr, int value) {
    if (arr->size >= arr->capacity) {
        int newCapacity = arr->capacity * 2;
        int *newData = (int*)realloc(arr->data, newCapacity * sizeof(int));
        if (newData == NULL) return;
        
        arr->data = newData;
        arr->capacity = newCapacity;
    }
    
    arr->data[arr->size++] = value;
}

int get(const DynamicArray *arr, int index) {
    if (index < 0 || index >= arr->size) return -1;
    return arr->data[index];
}

void freeArray(DynamicArray *arr) {
    if (arr != NULL) {
        free(arr->data);
        free(arr);
    }
}

int main() {
    DynamicArray *arr = createArray(2);
    
    for (int i = 0; i < 10; i++) {
        pushBack(arr, i * 10);
        printf("添加 %d, 大小: %d, 容量: %d\n", 
               i * 10, arr->size, arr->capacity);
    }
    
    printf("\n数组内容:\n");
    for (int i = 0; i < arr->size; i++) {
        printf("arr[%d] = %d\n", i, get(arr, i));
    }
    
    freeArray(arr);
    
    return 0;
}

动态栈

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

typedef struct Node {
    int data;
    struct Node *next;
} Node;

typedef struct {
    Node *top;
    int size;
} Stack;

Stack* createStack() {
    Stack *stack = (Stack*)malloc(sizeof(Stack));
    if (stack != NULL) {
        stack->top = NULL;
        stack->size = 0;
    }
    return stack;
}

void push(Stack *stack, int value) {
    Node *newNode = (Node*)malloc(sizeof(Node));
    if (newNode == NULL) return;
    
    newNode->data = value;
    newNode->next = stack->top;
    stack->top = newNode;
    stack->size++;
}

int pop(Stack *stack) {
    if (stack->top == NULL) return -1;
    
    Node *temp = stack->top;
    int value = temp->data;
    stack->top = temp->next;
    stack->size--;
    free(temp);
    
    return value;
}

int peek(const Stack *stack) {
    if (stack->top == NULL) return -1;
    return stack->top->data;
}

int isEmpty(const Stack *stack) {
    return stack->top == NULL;
}

void freeStack(Stack *stack) {
    while (!isEmpty(stack)) {
        pop(stack);
    }
    free(stack);
}

int main() {
    Stack *stack = createStack();
    
    push(stack, 10);
    push(stack, 20);
    push(stack, 30);
    
    printf("栈顶元素: %d\n", peek(stack));
    printf("栈大小: %d\n", stack->size);
    
    printf("\n出栈:\n");
    while (!isEmpty(stack)) {
        printf("弹出: %d\n", pop(stack));
    }
    
    freeStack(stack);
    
    return 0;
}

动态队列

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

typedef struct Node {
    int data;
    struct Node *next;
} Node;

typedef struct {
    Node *front;
    Node *rear;
    int size;
} Queue;

Queue* createQueue() {
    Queue *queue = (Queue*)malloc(sizeof(Queue));
    if (queue != NULL) {
        queue->front = NULL;
        queue->rear = NULL;
        queue->size = 0;
    }
    return queue;
}

void enqueue(Queue *queue, int value) {
    Node *newNode = (Node*)malloc(sizeof(Node));
    if (newNode == NULL) return;
    
    newNode->data = value;
    newNode->next = NULL;
    
    if (queue->rear == NULL) {
        queue->front = newNode;
        queue->rear = newNode;
    } else {
        queue->rear->next = newNode;
        queue->rear = newNode;
    }
    queue->size++;
}

int dequeue(Queue *queue) {
    if (queue->front == NULL) return -1;
    
    Node *temp = queue->front;
    int value = temp->data;
    queue->front = temp->next;
    
    if (queue->front == NULL) {
        queue->rear = NULL;
    }
    
    queue->size--;
    free(temp);
    
    return value;
}

int front(const Queue *queue) {
    if (queue->front == NULL) return -1;
    return queue->front->data;
}

int isEmpty(const Queue *queue) {
    return queue->front == NULL;
}

void freeQueue(Queue *queue) {
    while (!isEmpty(queue)) {
        dequeue(queue);
    }
    free(queue);
}

int main() {
    Queue *queue = createQueue();
    
    enqueue(queue, 10);
    enqueue(queue, 20);
    enqueue(queue, 30);
    
    printf("队首元素: %d\n", front(queue));
    printf("队列大小: %d\n", queue->size);
    
    printf("\n出队:\n");
    while (!isEmpty(queue)) {
        printf("出队: %d\n", dequeue(queue));
    }
    
    freeQueue(queue);
    
    return 0;
}

动态链表

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

typedef struct Node {
    int data;
    struct Node *next;
} Node;

typedef struct {
    Node *head;
    int size;
} LinkedList;

LinkedList* createList() {
    LinkedList *list = (LinkedList*)malloc(sizeof(LinkedList));
    if (list != NULL) {
        list->head = NULL;
        list->size = 0;
    }
    return list;
}

void insertAtHead(LinkedList *list, int value) {
    Node *newNode = (Node*)malloc(sizeof(Node));
    if (newNode == NULL) return;
    
    newNode->data = value;
    newNode->next = list->head;
    list->head = newNode;
    list->size++;
}

void insertAtTail(LinkedList *list, int value) {
    Node *newNode = (Node*)malloc(sizeof(Node));
    if (newNode == NULL) return;
    
    newNode->data = value;
    newNode->next = NULL;
    
    if (list->head == NULL) {
        list->head = newNode;
    } else {
        Node *current = list->head;
        while (current->next != NULL) {
            current = current->next;
        }
        current->next = newNode;
    }
    list->size++;
}

void deleteValue(LinkedList *list, int value) {
    Node *current = list->head;
    Node *prev = NULL;
    
    while (current != NULL) {
        if (current->data == value) {
            if (prev == NULL) {
                list->head = current->next;
            } else {
                prev->next = current->next;
            }
            free(current);
            list->size--;
            return;
        }
        prev = current;
        current = current->next;
    }
}

void printList(const LinkedList *list) {
    Node *current = list->head;
    while (current != NULL) {
        printf("%d -> ", current->data);
        current = current->next;
    }
    printf("NULL\n");
}

void freeList(LinkedList *list) {
    Node *current = list->head;
    while (current != NULL) {
        Node *temp = current;
        current = current->next;
        free(temp);
    }
    free(list);
}

int main() {
    LinkedList *list = createList();
    
    insertAtTail(list, 10);
    insertAtTail(list, 20);
    insertAtHead(list, 5);
    insertAtTail(list, 30);
    
    printf("链表内容: ");
    printList(list);
    printf("链表大小: %d\n", list->size);
    
    deleteValue(list, 20);
    printf("删除20后: ");
    printList(list);
    
    freeList(list);
    
    return 0;
}

内存检测工具

使用valgrind检测内存泄漏

bash
gcc -g program.c -o program
valgrind --leak-check=full ./program

常见valgrind输出

==12345== HEAP SUMMARY:
==12345==     in use at exit: 0 bytes in 0 blocks
==12345==   total heap usage: 3 allocs, 3 frees, 1,024 bytes allocated
==12345== 
==12345== All heap blocks were freed -- no leaks are possible

最佳实践

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

int* safeAllocate(size_t size) {
    int *ptr = (int*)malloc(size);
    if (ptr == NULL) {
        fprintf(stderr, "内存分配失败\n");
        exit(EXIT_FAILURE);
    }
    return ptr;
}

void safeFree(void **ptr) {
    if (ptr != NULL && *ptr != NULL) {
        free(*ptr);
        *ptr = NULL;
    }
}

int main() {
    int *data = safeAllocate(100 * sizeof(int));
    
    for (int i = 0; i < 100; i++) {
        data[i] = i;
    }
    
    printf("数据处理完成\n");
    
    safeFree((void**)&data);
    
    if (data == NULL) {
        printf("内存已安全释放\n");
    }
    
    return 0;
}

小结

动态内存管理的关键点:

函数用途
malloc分配未初始化内存
calloc分配并初始化为0
realloc调整内存大小
free释放内存

注意事项:

  • 每次malloc都要检查返回值
  • 每次malloc都要有对应的free
  • free后将指针置为NULL
  • 避免重复释放和越界访问

导航