堆区:
- 1、容量远大于栈的内存空间;
- 2、用于动态内存分配;
- 3、内存的申请和释放由程序员决定;
#include <stdio.h>
#include <stdlib.h>
int *func()
{
// 堆区申请空间,(注意:malloc申请的空间,需要手动释放,否则会造成内存泄漏)
int *tmp = malloc(sizeof(int)); // 定义指针接收malloc返回的地址
*tmp = 100; // 给堆区地址赋值
return tmp; // 函数调用完毕,返回堆区地址不释放
}
int main()
{
// 初始化指针,NULL防止出现野指针
int *p = NULL;
// 接收函数返回开辟的堆区数据100的地址
p = func();
// 解引用查看返回堆区地址的值
printf("*p = %d\n", *p);
// 堆区空间,使用完毕,手动释放
// 若内存没被释放则释放
if (p != NULL)
{
// free接收的是指向堆区的地址的指针
free(p);
// 手动配置指针为空指针
p = NULL;
}
return 0;
}
栈区:
- 1、先进后出的内存结构,由编译器自动分配释放;
- 2、存放函数的参数值、返回值、局部变量等;
- 3、局部变量的生存周期为申请到释放该段栈空间。
#include <stdio.h>
#include <stdlib.h>
int *func()
{
int a = 10;
return &a; // 函数调用完毕,因为a是局部变量,存储在栈区,a释放
}
int main()
{
// 初始化指针
int *p = NULL;
// 尝试接收func局部变量的地址,但是实际局部变量已经释放,导致p接收到一个野指针
p = func();
*p = 100; // 操作野指针指向的内存
printf("11111111111111111\n"); // 这句话可能执行不到,因为上一句话报错
return 0;
}
代码区:
- 可执行代码都加载到代码区,这块内存是不可以在运行期间修改的;
未初始化数据区(BSS):
- 主要是用来存放程序中未初始化和初始化为0的全局变量和静态变量的内存区域
全局区、静态区
- 加载的是可执行文件数据段,存储于数据段(全局初始化,静态初始化数据,文字常量(只读))的数据的生存周期为整个程序运行过程
局部变量会在函数执行结束的时候销毁,但是用static关键字声明的静态局部变量并不会,举例如下:
#include <stdio.h>
#include <stdlib.h>
int *func()
{
// 静态局部变量,只会初始化一次
static int a = 10;
return &a; // 函数调用完毕,a不释放,返回a的地址,外部以指针接收
}
int main() {
int *p = NULL;
// 由于静态局部变量并不会被释放,所以成功接收到了a的地址
p = func();
// 解引用修改a的值
*p = 100;
printf("*p = %d\n", *p);
return 0;
}
● 普通局部变量和静态局部变量区别
○ 存储位置:
■ 普通局部变量存储在栈上
■ 静态局部变量存储在静态存储区
○ 生命周期:
■ 当函数执行完毕时,普通局部变量会被销毁
■ 静态局部变量的生命周期则是整个程序运行期间,即使函数调用结束,静态局部变量的值也会被保留 ○ 初始值:
■ 普通局部变量在每次函数调用时都会被初始化,它们的初始值是不确定的,除非显式地进行初始化
■ 静态局部变量在第一次函数调用时会被初始化,然后保持其值不变,直到程序结束
- 局部变量和静态局部变量
void normal_func() {
int i = 0;
i++;
printf("局部变量 i = %d\n", i);
}
void static_func() {
static int j = 0;
j++;
printf("static局部变量 j = %d\n", j);
}
int main() {
// 调用3次normal_func()
// 局部变量结束销毁,并不会逐次累加
normal_func();
normal_func();
normal_func();
// 调用3次static_func()
// 静态变量结束保留,会随着调用函数逐次累加
static_func();
static_func();
static_func();
return 0;
}