Appearance
动态内存
概述
动态内存管理允许程序在运行时分配和释放内存,这对于处理不确定大小的数据结构非常重要。
内存区域
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
- 避免重复释放和越界访问