目录
一、内存使用方式
(一)一个c/c++编译的程序占用的内存分为以下几个部分
二、malloc
(一)malloc
1. 举例:malloc(4)
2. 如何理解malloc(size(Var_T)*N)
3. 举例
(二)静态、全局指针和malloc
二、free
(一)free释放内存前后
(二)将已经释放的值赋值NULL
(四)常见的动态内存错误
1. 对NULL指针的解引用操作
2. 对动态开辟空间的越界访问
3. 对非动态开辟内存使用free释放
4. 使用free释放一块动态开辟内存的一部分
5. 对同一块动态内存多次释放
6. 动态开辟内存忘记释放(内存泄漏)
三、calloc
(一)calloc
(二)用memset和malloc 实现calloc一样的功能
1. memset
2. 举例
四、realloc
(一)realloc在调整内存空间的是存在两种情况
1. 原有空间之后有足够大的空间
2. 原有空间之后没有足够大的空间
一、内存使用方式
(一)一个c/c++编译的程序占用的内存分为以下几个部分
- 代码区:存放程序的二进制代码
- 常量区:所有常量存放在常量区,程序结束后,由操作系统释放
- 静态区(全局区)(static):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量的存储是放在一块的,未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放
- 堆区(heap):一般由程序员分配释放,若程序员不释放,程序技术后可能由操作系统回收,与数据结构中的堆不一样
- 栈区(stack):由编译器自动分配释放、存放函数的参数值,局部变量的值等。其操作方法类似于数据结构中的栈
- 注意:凡是定义在方法中的都是局部变量(方法外的是全局变量)
二、malloc
- c语言函数库分别提供了两个函数,malloc和free分别用于动态内存分配和释放,这些函数维护一个可用的内存池。当一个程序另外需要一些内存时,它就调用malloc函数,malloc从内存池中提取一块合适的内存,并向该程序返回一个指向这块内存的指针。当分配的内存不再使用的时候,程序调用free函数把它归还给内存池。
(一)malloc
- malloc的参数就是需要分配的字节(字符)数,,如果内存池中的内存可以满足这个需求,malloc就返回一个指向被分配的内存块起始位置的指针
- malloc所分配的是一块连续的内存。同时,malloc实际分配内存有可能比请求的稍微多一些,但是这个行为是由编译器定义的
- 如果内存池是空的,或者它的可用内存无法满足需求时,malloc函数向操作系统请求,要求得到更多的内存,并在这块新内存上执行分配任务,如果操作系统无法向malloc提供更多的内存,malloc就返回一个NULL指针。因此,对每个从malloc返回的指针都需要进行检查,确保它并非NULL
- malloc又是如何知道所请求的内存需要存储的是整数、浮点数、结构还是数组呢?它并不知情---malloc返回一个类型为void*的指针,正是源于这个原因,标准表示一个void*类型的指针可以转换为其他任何类型的指针,但是,有些编译器,可能会要求在转换的时候使用强制类型转换
1. 举例:malloc(4)
- 表示从堆中申请4个字节的内存,并且这部分内存是连续的
(1)malloc()函数会返回一个void*类型的值,也就是分配出来的那块内存的第一个字节编号,也就是那块内存的首地址,通过首地址对那块内存进行访问
(2)void* p=malloc(4);
p指向那块内存的首地址,但是在利用*p定位内存的时候,由于变量p的返回类型是void*,定位出来的内存没有办法知道是什么变量类型,也就不知道大小等信息,因此malloc返回的值需要强制转换成一个有意义的变量类型。
例如:
int *p=(int*)malloc(4)
2. 如何理解malloc(size(Var_T)*N)
如:int a[10];
(1)变量a对应10个连续int组成的内存块,这块内存的表示值类型是int*
int*p=a;
(2)malloc(sizeof(int)*10)申请10个连续int的空间
解释为:申请一个int[10]空间,则该空间表示值类型应该为int*
int *p=(int*)malloc(sizeof(int)*10);
3. 举例
#include<iostream>
#include<cstdlib>
using namespace std;
int main()
{
int* ptr = NULL;
ptr = (int*)malloc(5 * sizeof(int));
if (ptr != NULL)//判断ptr指针是否为空
{
int i = 0;
for (i = 0; i < 5; i++)
{
*(ptr + i) = 0;
}
}
free(ptr);//释放ptr所指向的动态内存
ptr = NULL;
return 0;
}
(二)静态、全局指针和malloc
- 初始化静态或全局变量时,不能调用函数,下面的代码声明一个静态变量,并试图用malloc初始化:
static int* pi = malloc(sizeof(int));//会产生错误信息,对于全局变量也一样
- 对于静态变量,可以通过在后面用一个单独的语句给变量分配内存来避免这个问题
static int* pi;
pi = malloc(sizeof(int));
- 对于全局变量,不能用单独的赋值语句,因为全局变量在函数和可执行语句外部声明,而赋值语句这类代码必须出现在函数中
二、free
- free的参数是由malloc,calloc或者realloc函数分配的内存地址,这块内存会被返还给堆。尽管指针仍然指向这块区域,但是此时应该将它看成指向垃圾数据。稍后可能重新分配这块区域,并将其装进不同的数据
(一)free释放内存前后
//举例
#include <iostream>
#include<stdlib.h>
using namespace std;
int main()
{
int* p = (int*)malloc(sizeof(int));
if (p == NULL)
exit(-1);
*p = 20;
cout << &p << " " << p << endl;//000000C61DDAFAD8 000002B2956376C0
free(p);
cout << &p << " " << p;//000000C61DDAFAD8 000002B2956376C0
return 0;
}
- free函数执行前后瞬间内存的分配情况
- 虚线框表示内存已经释放,但是仍然有可能包含原值,p变量仍然指向地址000002B2956376C0,这种情况称为迷途指针
(二)将已经释放的值赋值NULL
int* p = (int*)malloc(sizeof(int));
free(p);
p = NULL;
- 这种方法可以解决迷途指针类问题
(四)常见的动态内存错误
1. 对NULL指针的解引用操作
void test()
{
int *p = (int *)malloc(INT_MAX/4);
*p = 20;//如果p的值是NULL,就会有问题
free(p);
}
2. 对动态开辟空间的越界访问
void test()
{
int i = 0;
int *p = (int *)malloc(10*sizeof(int));
if(NULL == p)
{
exit(EXIT_FAILURE);
}
for(i=0; i<=10; i++)
{
*(p+i) = i;//当i是10的时候越界访问
}
free(p);
}
3. 对非动态开辟内存使用free释放
//错误举例:
int num;
int* p = &mun;
free(p);
4. 使用free释放一块动态开辟内存的一部分
void test()
{
int *p = (int *)malloc(100);
p++;
free(p);//p不再指向动态内存的起始位置
}
5. 对同一块动态内存多次释放
int* p = (int*)malloc(sizeof(int));
int* p2 = p;
free(p);
.......
free(p2);
6. 动态开辟内存忘记释放(内存泄漏)
char* p;
while (1)
{
p = (char*)malloc(sizeof(char));
cout << endl;
}
- p变量指向堆上的内存。然而,在它指向另外一块内存之前没有释放这块内存,最终,程序会用光内存然后非正常终止,即使没有终止,至少内存的利用效率也不高
三、calloc
(一)calloc
- calloc也用于动态内存分配,calloc的参数包括所需元素的数量和每个元素的字节数,根据这些值,它能够计算出总共需要分配的内存
- calloc在返回指向内存的指针之前会把它初始化为0
- 与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0
#include <iostream>
#include<stdlib.h>
#include<assert.h>
using namespace std;
int main()
{
int* p = (int*)calloc(10, sizeof(int));
assert(p != NULL);
free(p);
p = NULL;
return 0;
}
(二)用memset和malloc 实现calloc一样的功能
1. memset
2. 举例
int* p = (int*)calloc(5, sizeof(int));
//等价形式
int* p = (int*)malloc(5 * sizeof(int));
memset(p, 0, sizeof(int));
四、realloc
- ptr 是要调整的内存地址
- size 调整之后新大小
- 返回值为调整之后的内存起始位置(新指针)。
- 这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到 新 的空间
- realloc函数用于修改一个原先已经分配的内存块大小
(一)realloc在调整内存空间的是存在两种情况
1. 原有空间之后有足够大的空间
- 要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化
2. 原有空间之后没有足够大的空间
- 原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小的连续空间来使用,把旧的内存复制到新区域,这样函数返回的是一个新的内存地址
#include <iostream>
#include<stdlib.h>
#include<assert.h>
using namespace std;
int main()
{
int* p = (int*)malloc(40);
assert(p != NULL);
int* p2 = (int*)realloc(p, 40);
assert(p2 != NULL);
p = p2;
return 0;
}