目录
前言
内存区域划分与分配
动态内存函数
malloc()函数
函数简介
free()函数
函数简介
calloc()函数
函数简介
realloc()函数
函数简介
1.栈区(stack)- 程序运行时由编译器自动分配,存放函数的形式参数,局部变量的值;
函数内的局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放;
2.堆区(heap)- 在内存开辟另一块存储区域;程序运行时用malloc() ,calloc(), realloc()或new申请任意多少的内存,程序员自己负责何时用free或delete释放内存;
3.静态区(static) - 编译器编译时即分配内存,用于存放全局变量和静态变量;这块内存在程序整个运行期间都存在;
4.文字常量区 - 用于存放常量字符串,程序结束后由系统释放;
5. 程序代码区 - 存放函数体的二进制代码;
malloc()函数的作用:
malloc()函数向内存申请一块连续可用的空间,如果开辟内存空间成功,返回指向这块空间的起始地址的指针,如果开辟内存空间失败,返回NULL指针(空指针);
参数解读:
1. malloc()函数返回值类型为void*,malloc()函数并不知道开辟空间的类型,使用时由使用者决定;
2. malloc()函数的参数为字节的个数,即通过malloc()函数申请的空间以字节为单位;
3. 头文件 # include
开辟成功的情况:
# include
int main()
{
//向内存申请10个整型的空间,即40个字节;
//malloc()函数返回值为void*,强制类型转换为int*
int* ptr = (int*)malloc(40);
int* pc = ptr;
//判断内存开辟是否成功
//开辟失败,打印失败原因
if (pc == NULL)
{
perror("malloc");
}
//开辟成功,开始使用
int i = 0;
for (i = 0; i < 10; i++)
{
*pc = i;
pc++;
}
return 0;
}
内存窗口:
开辟失败的情况:
# include
# include
int main()
{
int* ptr = (int*)malloc(INT_MAX);
int* pc = ptr;
//判断内存开辟是否成功
//开辟失败,打印失败原因
if (pc == NULL)
{
perror("malloc");
}
//开辟成功,开始使用
int i = 0;
for (i = 0; i < 10; i++)
{
*pc = i;
pc++;
}
return 0;
}
运行结果:
malloc()函数只负责分配内存,并不会对内存进行初始化,内存空间中的值是随机的;
# include
int main()
{
int* ptr = (int*)malloc(40);
int* pc = ptr;
if (pc == NULL)
{
perror("malloc");
}
//开辟成功,使用开辟好的内存空间
return 0;
}
内存窗口:
当malloc()函数分配的内存空间不再使用时,使用free()函数释放这块内存空间,当我们不释放动态申请的内存空间时
1. 如果程序结束,动态申请的内存会由操作系统自动回收;
2. 如果程序不结束,动态申请的内存不会自动回收,会导致内存泄漏的问题;
free()函数的作用:
专门用来处理动态内存的释放和回收,free()只能释放堆内存;
参数解读:
ptr是一个任意类型的指针变量,指向被释放区域的首地址;
注意事项:
1. free()函数的指针指向动态开辟的内存;例如malloc函数创建的内存,malloc()函数创建的连续的内存空间有多大,free()就释放多大的内存;
2. 如果ptr为空指针,则free(NULL) 啥事也不做;
3. 当动态开辟的内存空间被释放掉,free()函数的参数ptr仍然有能力找到这块空间;为避免使用者错误使用这块空间,将ptr置成空指针;
int main()
{
int* ptr = (int*)malloc(40);
int* pc = ptr;
//开辟失败,打印失败原因
if (pc == NULL)
{
perror("malloc");
}
//开辟成功,使用开辟好的内存空间
int i = 0;
for (i = 0; i < 10; i++)
{
*pc = i;
pc++;
}
//使用结束
free(ptr);
//当malloc()函数开辟的内存空间被释放掉,free()函数仍然有能力找到这块空间;
//避免使用者错误使用这块空间,将ptr置成空指针;
ptr = NULL;
return 0;
}
calloc()函数的作用:
calloc()函数向内存申请一块连续可用的空间,如果开辟内存空间成功,返回指向这块空间的 起始地址的指针,如果开辟内存空间失败,返回NULL指针(空指针),并且将这块空间的每个字节初始化为0;
参数解读:
1. calloc()函数返回值类型为void*,calloc()函数并不知道开辟空间的类型,使用时由使用者决定;
2. calloc()函数的第一个参数为元素的个数,第二个参数为每个元素类型所占的字节的大小;
3. 头文件 # include
int main()
{
//申请10个整型的空间
int*pc = (int *)calloc(10, sizeof(int));
return 0;
}
内存窗口:
40个字节全部初始化为0
int main()
{
//申请10个整型的空间
int*pc = (int *)calloc(10, sizeof(int));
//判断calloc()函数开辟空间是否成功
//开辟失败,显示失败原因
if (pc == NULL)
{
perror("calloc");
return 1;
}
//开辟成功,开始使用
int i = 0;
for (i = 0; i < 10; i++)
{
*(pc + i) = i;
}
//使用结束,释放动态开辟的内存空间
free(pc);
pc = NULL;
return 0;
}
realloc()函数的作用:
为了合理的使用内存,需要对内存的大小做出灵活的调整,realloc()函数用于对动态内存进行扩容(即已申请的动态内存空间不够使用,需要进行空间扩容操作)
参数解读:
1. ptr为要调整的内存地址
2. size_t size 为新开辟的内存空间的字节数,而不是在原来字节数的基础上增加的字节数;
3. 返回值为调整之后内存的起始位置
先不考虑开辟失败的情况,假设开辟成功,观察如下现象:
# include
int main()
{
//使用malloc()开辟40个字节
int*pc = (int*)malloc(40);
//使用realloc()函数进行扩容,扩容后大小为10000个字节;
int* pa = realloc(pc, 10000);
printf("pc的地址: 0x%p\n pa的地址:0x%p\n", pc, pa);
return 0;
}
运行结果:
# include
int main()
{
//使用malloc()开辟40个字节
int*pc = (int*)malloc(40);
//使用realloc()函数进行扩容,扩容后大小为100个字节;
int* pa = realloc(pc, 100);
printf("pc的地址: 0x%p\n pa的地址:0x%p\n", pc, pa);
return 0;
运行结果:
如图所示,扩容后的地址和原先的地址有可能相同,有可能不同,但是这仅仅取决于扩容内存的大小;
case 1:当原有空间的后面有足够大的空间,扩展的内存就直接在原有的内存之后追加空间,原来空间的数据不发生变化;
case 2:当原有空间的后面没有足够大的空间,扩展的方法是在堆空间上另找一块合适大小的连续空间来使用,返回的是新的内存地址并且将原来内存中的数据移动到新的内存空间;
int main()
{
int* pc = (int*)malloc(40);
if (pc == NULL)
{
perror("malloc");
}
int i = 0;
for (i = 0; i < 10; i++)
{
*(pc + i) = i;// 0 1 2 3 4 5 6 7 8 9
}
//空间不够,希望能放20个元素;
int* ptr = (int*)realloc(pc, 80);
//判断扩容是否成功
//扩容失败
if (ptr == NULL)
{
perror("realloc");
return 1;
}
//扩容成功,开始使用
//使用结束,释放内存
free(ptr);
ptr = NULL;
return 0;
}