文章目录
动态内存
int a=0;
char c[20]={0};
//int 在栈帧空间开辟四字节
//char 在栈帧空间开辟二十字节
动态内存函数
栈区:局部变量,函数形式参数
堆区:动态内存管理
malloc
<stdlib.h>
void* malloc (size_t size)
用于申请内存空间,返回一个指针返回指向空间的首地址
如果申请失败将会返回空指针
当开辟的空间过大就会出现失败的情况
如果参数为零是未定义的,结果取决于编译器
开辟失败
void* malloc(size_t size);
//在堆上开辟size个字节的空间
int main()
{
//申请40个字节
int*p=(int*)malloc(40);
//因为malloc函数的返回值为void*所以要强制转化
if(p==NULL)
//开辟失败返回空指针
{
printf("%s\n",streeor(errno));
return 1;
}
int n=0;
for(n=0;n<10;n++)
{
//使用malloc开辟的空间来存放1~10
*(P+1)=n+1;
}
//打印
for(n=0;n<10;n++)
{
printf("%d ",*(p+n));
}
return 0;
}
free
<stdlib.h>
void free(void*ptr)
释放内存,参数为释放空间的首地址
只能释放动态内存释放的空间,如果传如参数是空指针,函数将无作为
//malloc开辟的空间会在程序结束后销毁
//但是程序内容过多,这片空间用不上的情况下,就是典型的“占着茅坑不拉屎”
//所以可以使用free函数来主动释放掉这块内存
int main()
{
int* p=(int*)malloc(40);
//开辟40字节空间
free(p);
//释放掉这片空间
//但是p其实还是指向了这块空间的起始位置的地址
p=NULL;
}
calloc
<stdlib.h>
void* calloc(size_t num,size_t size)
num为开辟元素个数
size每个元素大小
开辟空间失败,返回空指针
申请好空间后,会把空间初始化为零,然后返回起始地址
int main()
{
int* p=(int*)calloc(10,sizeof(int));
if(NULL==p)
{
perror("calloc");
//打印错误信息
return 1;
}
return 0;
}
开辟失败
calloc和malloc的区别
malloc开辟空间没有初始化直接返回起始位 地址
calloc开辟空间值初始化为零后返回起始位地址
realloc
重新调整内存块
realloc
<stdio.h>
void* realloc (void*ptr,size_t size)
ptr指向malloc,calloc,realloc开辟的空间
size开辟空间的大小 (字节)
当relloc的第一个参数为NULL,它的作用就和malloc相当
当ptr指向空间足够扩容,将会直接在当前空间后接上新空间,返回旧空间地址
当ptr指向空间不够扩容,将会开辟一片满足条件的新连续空间,并把原空间的数据拷贝到新扩容空间之前的位置,再把原空间给释放掉后返回新空间的地址
扩容失败返回NULL
所以在接收realloc的返回地址要用新的指针变量,防止使用原来变量接收返回的空指针,导致原指针数据丢失
int main()
{
int* p = (int*)malloc(5 * sizeof(int));
if (p == NULL)
{
perror("malloc");
return 1;
}
int i = 0;
for (i = 0; i < 5; i++)
{
*(p + i) = i + 1;
}
int* ptr = (int*)realloc(p, 5 * sizeof(int));
//继续使用空间
if (ptr != NULL)
{
p = ptr;
ptr = NULL;
}
for (i = 0; i < 10; i++)
{
printf("%d ",*(p+i));
}
free(p);
p = NULL;
return 0;
}
动态内存错误操作
对空指针解引用
int main()
{
int* p=(int*)malloc(40);
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = 0;
}
return 0;
}
### 动态空间的连续访问
int main()
{
int* p=(int*)malloc(10);
if (p == NULL)
{
return 1;
}
//越界访问
int i = 0;
for (i = 0; i <=100; i++)
{
p[i] = 0;
}
free(p);
p = NULL;
return 0;
}
对非动态内存使用free释放
int main()
{
int a = 10;
int* p = &a;
free(p);
p = NULL;
return 0;
}
使用free释放动态内存的一部分
int main()
{
int* p = (int*)malloc(10);
if (p == NULL)
{
return 1;
}
int i = 0;
for (i = 0; i < 5; i++)
{
*(p) = i;
p++;
}
//此时p指向循环结束的位置
free(p);
p = NULL;
return 0;
}
对动态内存多次释放
int main()
{
int* p=(int*)malloc(100);
if(p=NULL)
{
return 1;
}
free(p);
//释放后p还是指向原空间地址
free(p);
return 0;
}
动态内存忘记释放(内存泄露)
动态内存开辟不释放就会一直浪费内存
void test()
{
int* p = (int*)malloc(40);
if (p == NULL)
{
return;
}
//函数结束内存没有释放,主函数内也无法释放
//所以在使用后要直觉使用
//free(p);
//p=NULL;
//如果返回指针,在主函数内释放
//if(1)
//{
// return;
//}
//来不及释放
//free(p);
// p=NULL;
}
int main()
{
/*int* b =*/ test();
//free(b);
//b=NULL;
return 0;
}
柔性数组
C99中,结构中的最后一个元素允许是位置大小的数组,这就叫做柔性数组成员
结构体中的柔性数组成员之前必须至少有一个其他成员
计算结构体的大小时不包含柔性数组大小
包含柔性数组的结构体用malloc函数开辟动态内存时,开辟的空间应该大于结构体的大小,以适应柔性数组的预期大小
typedef struct S
{
int a;
char b;
char c[0];//柔性数组,表示数组大小是未知的
//cahr c[];意义是一样的
}typ_s;
int main()
{ //多开辟了十个字节
typ_s* ptr=malloc(sizeof(typ_s)+10*sizeof(char));
if(ptr==NULL)
{
perror("malloc");
return;
}
int sz=sizeof(typ_s);
ptr->n=100;
int i=0;
for(i;i<10;i++)
{
ptr->c[i]='1';
printf("%c\n",c[i]);
}
typ_s*ptt=(typ_s*)realloc(ptr,sizeof(typ_s)+20*sizeof(char));
if(ptt!=NULL)
{
ptr=ptt;
printf("增容\n");
}
else
{
perror("perror");
return 1;
}
free(ptr);
ptr=NULL;
return 0;
}
这片空间是连续的
方便内存释放,访问速度更快
typedef struct S
{
int i;
char* c;
}typ_s;
int main()
{
typ_s* ptr = (typ_s*)malloc(sizeof(typ_s));
if (ptr == NULL)
{
perror("malloc");
return 1;
}
ptr->c = (char*)malloc(sizeof(char) * 10);
if (ptr->c == NULL)
{
perror("malloc->c");
return 1;
}
ptr->i = 100;
int i = 0;
for (i; i < 10; i++)
{
ptr->c[i] = 'A';
printf("%c\n", ptr->c[i]);
}
char* ptt = (char*)realloc(ptr->c, sizeof(char) * 20);
if (ptt != NULL)
{
ptr->c = ptt;
}
else
{
perror("realloc::ptt");
return 1;
}
free(ptr->c);
ptr->c = NULL;
free(ptr);
ptr = NULL;
return 0;
}
用指针也可以实现,但是内存不连续,浪费内存