1. 静态存储区(Static Storage)
特点:
- 在程序编译时分配内存,生命周期持续到程序结束。
- 存放 全局变量 和 静态变量(
static
修饰的变量)。 - 默认初始化为零(未显式初始化时)。
示例:
#include <stdio.h>int global_var; // 全局变量(静态存储区)
static int static_var; // 静态变量(静态存储区)void func() {static int count = 0; // 静态局部变量(生命周期持续到程序结束)count++;printf("count = %d\n", count);
}int main() {printf("global_var = %d\n", global_var); // 输出 0(默认初始化)func(); // 输出 count = 1func(); // 输出 count = 2(静态变量保留上次的值)return 0;
}
2. 栈内存(Stack)
特点:
- 由编译器自动分配和释放,用于 局部变量 和函数调用。
- 内存空间有限(默认几MB),生命周期与函数作用域一致。
- 分配速度快,但超出作用域后内存会被自动回收。
示例:
#include <stdio.h>void stack_example() {int a = 10; // 栈内存分配char str[] = "Hello"; // 栈内存分配(数组)printf("a = %d, str = %s\n", a, str);
} // 函数结束时,a和str的内存自动释放int main() {stack_example(); // 输出 a = 10, str = Helloreturn 0;
}
3. 堆内存(Heap)
特点:
- 由程序员手动分配和释放(
malloc
,calloc
,realloc
+free
)。 - 生命周期完全由代码控制,空间大但管理不当会导致内存泄漏或野指针。
- 适合动态分配不确定大小的内存(如链表、树等数据结构)。
示例:
#include <stdio.h>
#include <stdlib.h>int main() {int *heap_var = (int *)malloc(sizeof(int)); // 堆内存分配*heap_var = 100;printf("heap_var = %d\n", *heap_var); // 输出 100free(heap_var); // 必须手动释放!// 动态数组示例int n = 5;int *arr = (int *)malloc(n * sizeof(int));for (int i = 0; i < n; i++) arr[i] = i;free(arr); // 释放数组内存return 0;
}
4. 常量存储区(Constant Storage)
特点:
- 存放 字符串常量 和
const
修饰的常量。 - 内存只读,修改会导致未定义行为(如程序崩溃)。
- 生命周期与程序一致。
示例:
#include <stdio.h>int main() {const int MAX = 100; // 常量(可能存储在常量区或栈,取决于编译器)char *str = "Hello World"; // 字符串常量(存储在常量区)// 以下行为是非法的!// str[0] = 'h'; // 修改常量区数据会导致段错误(Segmentation Fault)return 0;
}
5. 内存分配对比表
分配方式 | 生命周期 | 管理方式 | 典型用途 |
---|---|---|---|
静态存储区 | 程序整个运行期间 | 编译器自动管理 | 全局变量、静态变量 |
栈内存 | 函数作用域内 | 编译器自动管理 | 局部变量、函数参数 |
堆内存 | 直到调用free() 释放 |
程序员手动管理 | 动态数据结构(链表、树等) |
常量存储区 | 程序整个运行期间 | 只读,不可修改 | 字符串常量、const 常量 |
6. 常见问题与注意事项
- 栈溢出:递归深度过大或局部变量占用空间过多会导致栈溢出(Stack Overflow)。
- 内存泄漏:未释放堆内存会导致程序占用内存持续增长。
- 野指针:释放堆内存后未将指针置为
NULL
,可能引发非法访问。 - 常量区修改:试图修改字符串常量会导致未定义行为。
示例(错误用法):
int *create_array() {int arr[3] = {1, 2, 3}; // 栈内存return arr; // 错误!返回栈内存地址,调用后arr已被释放
}int main() {int *p = create_array();printf("%d\n", p[0]); // 输出随机值(悬垂指针)return 0;
}