C语言通过标准库函数管理堆内存:
| 函数 | 功能 | 特点 |
|---|---|---|
malloc |
分配内存 | 不初始化,内容随机 |
calloc |
分配并清零 | 初始化为0 |
realloc |
调整大小 | 可能迁移内存位置 |
free |
释放内存 | 必须使用分配的指针 |
| 函数 | 功能 | 注意事项 |
|---|---|---|
memcpy |
内存复制 | 不处理重叠 |
memmove |
内存移动 | 处理重叠情况 |
memset |
内存设置 | 按字节设置 |
memcmp |
内存比较 | 按字节比较 |
| 特性 | 栈内存 | 堆内存 |
|---|---|---|
| 分配速度 | 快(移动栈指针) | 慢(系统调用) |
| 大小限制 | 较小(通常几MB) | 较大(受物理内存限制) |
| 生命周期 | 函数结束自动释放 | 需手动free |
| 碎片问题 | 无 | 可能产生碎片 |
嵌入式系统中常用内存池(Memory Pool)预分配固定大小的内存块,避免动态分配的不确定性和碎片问题。
#include <stdlib.h>
// 内存分配
void *malloc(size_t size);
void *calloc(size_t nmemb, size_t size);
void *realloc(void *ptr, size_t size);
void free(void *ptr);
#include <string.h>
// 内存操作
void *memcpy(void *dest, const void *src, size_t n);
void *memmove(void *dest, const void *src, size_t n);
void *memset(void *s, int c, size_t n);
int memcmp(const void *s1, const void *s2, size_t n);
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void demonstrate_malloc(void) {
printf("=== malloc ===\n");
int *arr = malloc(5 * sizeof(int));
if (arr == NULL) {
fprintf(stderr, "malloc failed\n");
return;
}
// 注意:malloc不初始化,内容随机
printf("Uninitialized values: ");
for (int i = 0; i < 5; i++) {
printf("%d ", arr[i]);
}
printf("\n");
// 手动初始化
for (int i = 0; i < 5; i++) {
arr[i] = i * 10;
}
printf("After init: ");
for (int i = 0; i < 5; i++) {
printf("%d ", arr[i]);
}
printf("\n");
free(arr);
arr = NULL; // 防止野指针
}
void demonstrate_calloc(void) {
printf("\n=== calloc ===\n");
int *arr = calloc(5, sizeof(int));
if (arr == NULL) {
fprintf(stderr, "calloc failed\n");
return;
}
// calloc自动初始化为0
printf("Zero-initialized: ");
for (int i = 0; i < 5; i++) {
printf("%d ", arr[i]);
}
printf("\n");
free(arr);
arr = NULL;
}
void demonstrate_realloc(void) {
printf("\n=== realloc ===\n");
int *arr = malloc(3 * sizeof(int));
if (arr == NULL) return;
for (int i = 0; i < 3; i++) arr[i] = i;
printf("Original (3): ");
for (int i = 0; i < 3; i++) printf("%d ", arr[i]);
printf("\n");
// 扩容到6
int *temp = realloc(arr, 6 * sizeof(int));
if (temp == NULL) {
fprintf(stderr, "realloc failed\n");
free(arr);
return;
}
arr = temp;
// 新空间可能未初始化
for (int i = 3; i < 6; i++) arr[i] = i * 10;
printf("Expanded (6): ");
for (int i = 0; i < 6; i++) printf("%d ", arr[i]);
printf("\n");
free(arr);
arr = NULL;
}
void demonstrate_mem_functions(void) {
printf("\n=== 内存操作函数 ===\n");
// memcpy
int src[] = {1, 2, 3, 4, 5};
int dest[5];
memcpy(dest, src, sizeof(src));
printf("memcpy: ");
for (int i = 0; i < 5; i++) printf("%d ", dest[i]);
printf("\n");
// memmove(处理重叠)
int data[] = {1, 2, 3, 4, 5, 6, 7, 8};
memmove(data + 2, data, 6 * sizeof(int)); // 重叠复制
printf("memmove: ");
for (int i = 0; i < 8; i++) printf("%d ", data[i]);
printf("\n");
// memset
char buffer[20];
memset(buffer, 'A', sizeof(buffer) - 1);
buffer[sizeof(buffer) - 1] = '\0';
printf("memset: %s\n", buffer);
// memcmp
int a[] = {1, 2, 3};
int b[] = {1, 2, 4};
printf("memcmp: %d\n", memcmp(a, b, 3 * sizeof(int)));
}
void demonstrate_memory_leak(void) {
printf("\n=== 内存泄漏示例 ===\n");
// 危险:覆盖指针导致泄漏
int *p = malloc(100 * sizeof(int));
printf("Allocated at %p\n", (void *)p);
// 错误:覆盖指针,原内存泄漏
// p = malloc(200 * sizeof(int)); // 原内存无法释放!
free(p);
p = NULL;
printf("Freed and set to NULL\n");
}
int main() {
demonstrate_malloc();
demonstrate_calloc();
demonstrate_realloc();
demonstrate_mem_functions();
demonstrate_memory_leak();
return 0;
}
编译命令:
gcc -Wall -Wextra -o memory_manage memory_manage.c
./memory_manage
p = realloc(p, size),应先用临时变量接收memset(arr, 0, n)只适用于字节为0的情况Q1: malloc和calloc有什么区别? A: malloc不初始化内存,calloc初始化为零。calloc参数是元素数量和大小,malloc是总字节数。calloc可能稍慢。
Q2: realloc扩容时会发生什么? A: 如果原空间后有足够空间,直接扩展;否则分配新空间,复制数据,释放原空间。因此realloc后指针可能改变。
Q3: 如何检测内存泄漏? A: 使用valgrind、AddressSanitizer等工具。良好的习惯是malloc和free配对,free后置NULL,使用智能指针(C++)。
Q4: 什么时候用栈,什么时候用堆? A: 小型、生命周期短的数据用栈(快、自动管理)。大型、动态大小、需要跨函数生命周期的数据用堆。
Q5: memcpy和memmove有什么区别? A: memcpy不处理内存重叠,重叠时行为未定义。memmove处理重叠,更安全但可能稍慢。不确定是否重叠时用memmove。