文章目录
- 1.Linux内存分布图
- 2.C标准库中动态内存分配函数
- 3.动态内存分配函数的常见错误
1.Linux内存分布图
在程序设计当中,可以定义全局变量也可以定以局部变量,分别也是在全局区、栈区开辟,那么这些区域都不有用我们动手管理,但是我们在堆上动态开辟的空间需要我们手动管理!
当然,在程序设计中使用的是虚拟内存,在操作系统层面上是按照页的方式分配空间的,所有即使我们向堆申请1个字节的空间都不仅仅只是一个字节,所以小空间的变量要使用malloc来分配!
- 代码区(text):二进制代码,字面值常量,const修饰的具有常属性变量
- 数据区(data):包括已初始化的全局(包括静态和非静态)变量和静态局部变量
- BSS区:包括未初始化的(包括静态和非静态)变量和静态局部变量(进程成加载此区即被清0)注:有时候静数据区和BSS区被合称为全局区或静态区
- 堆(heap):动态内存分配,从低地址向高地址扩展,程序员动手管理的区域
- 文件映射或共享区:动静态库
- 栈(stack):非静态的局部变量,函数参数和返回值,从高地址向低地址扩展;栈的大小有一个固定值,可以通过调系统参数改变
- 命令行参数和环境变量
测试代码:
#include<stdio.h>
#include<stdlib.h>
int g_val = 5;
static int static_g_val = 5;
const int const_g_val = 5;
static const int static_const_g_val = 5;
int g_unval;
int main(int argc,char* argv[],char* env[])
{
static int static_tmp = 10;
const char* constant = "hello world";
printf("env addr:%p\n",env[0]);
printf("argv addr:%p\n",argv[0]);
char* heap1 = (char*)malloc(5);
char* heap2 = (char*)malloc(5);
char* heap3 = (char*)malloc(5);
printf("statck1 addr:%p\n",&heap1);
printf("statck2 addr:%p\n",&heap2);
printf("heap3 addr: %p\n",heap3);
printf("g_unval adrr:%p\n",&g_unval);
printf("static_tmp adrr:%p\n",&static_tmp);
printf("static_g_val adrr:%p\n",&static_g_val);
printf("g_val adrr:%p\n",&g_val);
printf("constant adrr:%p\n",constant);
printf("static_const_g_val adrr:%p\n",&static_const_g_val);
printf("const_g_val adrr:%p\n",&const_g_val);
printf("code adrr:%p\n",&main);
return 0;
}
输出结果:
env addr:0x7ffe3ca5f812
argv addr:0x7ffe3ca5f809
statck1 addr:0x7ffe3ca5f228
statck2 addr:0x7ffe3ca5f220
heap3 addr: 0x1277050
g_unval adrr:0x60104c
static_tmp adrr:0x601044
static_g_val adrr:0x601040
g_val adrr:0x60103c
constant adrr:0x400790
static_const_g_val adrr:0x400888
const_g_val adrr:0x400884
code adrr:0x40059d
2.C标准库中动态内存分配函数
void* malloc (size_t size)
功能:在堆上申请一块连续可用的空间
参数:申请的字节数,如是0,标准未定义,有编译器决定
返回值:返回指向这块空间的指针,否则返回NULL
void* calloc (size_t num, size_t size);
介绍:函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0
与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0。
关于
realloc
函数:
void* realloc (void* ptr, size_t size);
功能:realloc 函数就可以做到对动态开辟内存大小的调整。
参数:
ptr 是要调整的内存地址
size 调整之后新大小
情况2:原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小的连续空间来使用
free函数
void free (void* ptr);
如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的;如果参数 ptr 是NULL指针,则函数什么事都不做
在64位Linux机器,用的是glibc2.7版本:注:在Windows下测试是由很大不同的
示例代码:
```c
int main()
{
int* ptr1 = (int*)malloc(sizeof(int));
int* ptr2 = (int*)malloc(sizeof(int));
double* ptr3 = (double*)malloc(sizeof(double));
printf("ptr1:%x ,ptr2:%x ,ptr3:%x\n", ptr1, ptr2,ptr3);
free(ptr2);
ptr2 = NULL;
int* ptr4 = (int*)malloc(sizeof(int));
printf("ptr1:%x ,ptr3:%x ,ptr4:%x\n", ptr1, ptr3,ptr4);
void *ptr5 = realloc(ptr4,25/*25是一个阈值*/);
printf("ptr1:%x ,ptr3:%x ,ptr5:%x\n", ptr1, ptr3,ptr5);
return 0;
}
输出结果:
ptr1:24d1010 ,ptr2:24d1030 ,ptr3:24d1050
ptr1:24d1010 ,ptr3:24d1050 ,ptr4:24d1030
ptr1:24d1010 ,ptr3:24d1050 ,ptr5:24d1070
每个地址都是16的整数倍;而且指针之间有很大的空间比如,ptr1
和ptr2
隔着32个字节比int
4个字节要多不少!
为什么会输出这样的结果,另外free的参数只传入一个指针它怎么知道要释放多少个字节
使用malloc
函数申请空间,在返回地址前(当然,有可能在后面,不同的实现不同)有个控制信息,记录着malloc
开辟空间的管理信息,比如,开辟空间的大小,所以在free
释放的时候会根据传入的地址读取控制信息!malloc开
辟空间,会根据一定的对齐规则来管理!显然这里地址都是16的整数倍,对齐数是16!
总的来说:malloc根据一定的对齐规则,开辟空间并自动的开辟了对应的控制信息管理开辟的空间!当然,malloc一般申请的是33页,然后free不会立刻归还系统!
ptr4 和ptr2的指针为什么相同
free释放的资源不会立刻归还给系统,如果对一块空间释放,那么下次申请资源如果释放的空间大小合适,会通过双链表查找到释放的位置重新利用,这也避免一定的内碎片问题!
3.动态内存分配函数的常见错误
- 函数返回值都是指针,避免申请的失败对空指针的解引用
- 对动态开辟空间的越界访问
- 对非动态开辟内存使用free释放
- 使用free释放一块动态开辟内存的一部分
- 对同一块动态内存多次释放
- 动态开辟内存忘记释放导致内存泄漏问题