堆区
头文件:#include<stdlib.h>
一、void * malloc(size_t size)和 void free(void* ptr):
1、void * malloc(size_t size)
在堆区分配一块指定大小的内存空间,用来存放数据。这块内存空间在函数执行完成后不会被初始化,它们的值是未知的。
1).malloc()函数会向堆中申请一片连续的可用内存空间
2).若申请成功 ,,返回指向这片内存空间的指针 ,若失败 ,则会返回NULL, 所以我们在用malloc()函数开辟动态内存之后, 一定要判断函数返回值是否为NULL.
3).返回值的类型为void*型, malloc()函数并不知道连续开辟的size个字节是存储什么类型数据的 ,所以需要我们自行决定 ,方法是在malloc()前加强制转 ,转化成我们所需类型 ,如: (int*)malloc(sizeof(int)*n).
4).如果size为0, 此行为是未定义的, 会发生未知错误, 取决于编译器
2、void free(void* ptr):
内存释放:
free(p);
p=NULL;
在堆中申请的内存空间不会像在栈中存储的局部变量一样 ,函数调用完会自动释放内存 , 如果我们不手动释放, 直到程序运行结束才会释放, 这样就可能会造成内存泄漏, 即堆中这片内存中的数据已经不再使用, 但它一直占着这片空间, (通俗说就是就是占着茅坑不拉屎), 所以当我们申请的动态内存不再使用时 ,一定要及时释放 .
1).如果ptr没有指向使用动态内存分配函数分配的内存空间,则会导致未定义的行为。
2).如果ptr是空指针,则该函数不执行任何操作。
3).此函数不会更改ptr本身的值,因此它仍指向相同(现在已经无效)的位置(内存)
4).在free()函数之后需要将ptr再置空 ,即ptr = NULL;如果不将ptr置空的话 ,后面程序如果再通过ptr会访问到已经释放过无效的或者已经被回收再利用的内存, 为保证程序的健壮性, 一般我们都要写ptr = NULL; .
/*
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *p = (int*)malloc(sizeof(int) * 10); //申请10个int大小的空间
int size = _msize(p);//返回在堆中分配的内存块的大小 size_t 原型为 unsigned int
//printf("malloc分配内存大小:size=%d\n",size);
if (p == NULL)
{
exit(1);
}
printf("申请的内存空间大小为:%d\n", size);
p = (int *)realloc(p, sizeof(int) * 100);
size = _msize(p);
printf("重新申请的内存空间大小为:%d", size);
free(p);
return 0;
}
*/
#include <stdlib.h> // For _MAX_PATH definition
#include <stdio.h>
int main()
{
char *p;
p = (char*)malloc(_MAX_PATH); //#define _MAX_PATH 260
int size = _msize(p);//返回在堆中分配的内存块的大小 size_t 原型为 unsigned int
if (p == NULL)
printf("内存申请失败\n");
else
{
printf("内存申请成功\n");
printf("申请的内存空间大小为:%d\n", size);
free(p);
p = NULL;
printf("内存释放成功\n");
}
return 0;
}
运行结果:
二、void * calloc(size_t num,size_t size)
与malloc()函数的区别只在于, calloc()函数会在返回地址之前将所申请的内存空间中的每个字节都初始化为0 .
1).calloc()函数功能是动态分配num个大小(字节长度)为size的内存空间。
2).若申请成功 ,,返回指向这片内存空间的指针 ,若失败 ,则会返回NULL, 所以我们在用calloc()函数开辟动态内存之后, 一定要判断函数返回值是否为NULL。
3).返回值的类型为void*型, calloc()函数虽然分配num个size大小的内存空间 ,但还是不知道存储的什么类型数据 ,所以需要我们自行决定 ,方法是在calloc()前加强制转 ,转化成我们所需类型 ,如: (int*)calloc(num, sizeof(int))。
4).如果size与num有一个或都为0, 此行为是未定义的, 会发生未知错误, 取决于编译器。
/*
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *p = (int*)malloc(sizeof(int) * 10); //申请10个int大小的空间
int size = _msize(p);//返回在堆中分配的内存块的大小 size_t 原型为 unsigned int
//printf("malloc分配内存大小:size=%d\n",size);
if (p == NULL)
{
exit(1);
}
printf("申请的内存空间大小为:%d\n", size);
p = (int *)realloc(p, sizeof(int) * 100);
size = _msize(p);
printf("重新申请的内存空间大小为:%d", size);
free(p);
return 0;
}
*/
#include <stdlib.h> // For _MAX_PATH definition
#include <stdio.h>
int main()
{
int n;
char *p;
printf("请输入要动态分配多少个char类型存储空间:\n");
scanf("%d", &n);
p = (char*)calloc(n,sizeof(char));
int size = _msize(p);//返回在堆中分配的内存块的大小 size_t 原型为 unsigned int
if (p == NULL)
printf("内存申请失败\n");
else
{
printf("内存申请成功\n");
printf("申请的内存空间大小为:%d\n", size);
}
//输出char *p的内容
printf("%s\n", *p);
free(p);
p = NULL;
printf("内存释放成功\n");
return 0;
}
运行结果:
三、void * realloc(void * ptr,size_t size)
realloc()函数让动态内存管理更加灵活 .在程序运行过程中动态分配内存大小, 如果分配的太大 ,则浪费空间, 如果太小, 可能还是会出现不够用的情况 .为了合理的利用内存,我们一定会对内存的大小做灵活的调整。那realloc() 函数就可以做到对动态开辟内存大小的调整(既可以往大调整, 也可以往小了调整) .
1).ptr为需要调整的内存地址
2).size为调整后需要的大小(字节数)
3).若调整成功, 返回值为调整大小后内存的起始位置(也就是指向调整后内存的指针), 若失败(当没有内存可以分配时, 一般不会出现), 则返回NULL, 所以还是要对返回值判空
4).如果ptr是空指针, 则和calloc()函数一样作用一样
注意 : realloc()函数在扩大内存空间时有两种情况
1).ptr所指的内存后有足够的内存空间用来扩展 ,如图 :
2).ptr所指内存后没有足够的空间来扩展 ,如图 :
当第二种情况时, 若申请新的内存空间成功, 会将ptr所指向的内存中的内容拷贝到新的内存空间中, ptr所指向的内存会被释放, 返回新得内存地址, 若不成功 ,ptr 所指内存不会被释放, 函数返回NULL
四、小结
1).malloc()和calloc()函数用法一样, 唯一的区别是calloc()会对所申请内存的每个字节初始化为0
2).malloc(), calloc(), realloc()申请的内存不再使用时 ,一定要用free()释放 ,否则会造成内存泄漏
3).p = realloc(ptr, size)函数返回值不为空时, 释放内存时不需写free(ptr) ,只需写free(p)
参考:
野指针:
什么是野指针?野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)!
1、指针变量在定义时未初始化:
int *p; //定义之后未初始化是一个野指针
printf("%d",*p);
2、释放完指针所指向的内存之后未置空:
int *p = (int*)malloc(sizeof(int));
free(p); //释放完之后未置空 p=NULL;
printf("%d",*p);
怎么办?
初始化时置 NULL
int *p=NULL;
释放时置NULL
free(p);
p=NULL;