1.calloc
int main()
{
int n=5;
//int *ip=(int*)malloc(sizeof(int)*n);
int *ip=(int*)calloc(sizeof(int)*n);
for(int i=0;i<n;i++)
{
printf("&x\n",ip[i]);//ip[i]=>*(ip+i)//加一表示加了一个整形字节数
}
free(ip);//释放的是ip所指向的空间还给系统,并不是把ip释放掉
ip=nullptr;
return 0;
}
my_calloc()
void* my_calloc(size_t num,size_t size)
{
void* s=malloc(num*size);
if(s!=nullptr)
{
memset(s,0,num*size);//只有调用该函数,才能变成0
}
return s;
}
free
//vs 2019
int main()
{
int* ip=(int*)malloc(sizeof(int));
printf("%x\n",*ip);//cd cd cd cd//随机填充
*ip=10;
printf("%x\n",*ip);//00 00 00 0a
free(ip);
printf("%x\n",*ip);//ef ef ef ef//随机填充
*ip=100;
printf("%x\n",*ip);//00 00 00 64
free(ip);
return 0;
}
不能对同一个空间释放两次
int main()
{
int n=10;
int* ip=(int*)malloc(sizeof(int)*n);
if(ip==nullptr) exit(EXIT_FAILURE);
for(inti=0;i<n;++i)
{
ip[i]=i;
}
ip=(int*)realloc(ip,sizeof(int)*5);
free(ip);
return 0;
}
2.realloc
realloc扩充之前分配的内存块(重新分配内存块)
void *realloc(void *ptr,size_t new_size)
重新分配给定的内存区域。它必须是之前位malloc()、calloc()或realloc()所分配,并且仍未被free或realloc的调用所释放。否则,结果未定义。
重新分配按以下二者之一执行:
a>可能的话,扩张或收缩ptr所指向的已存在内存。内容在新旧大小中的较小者范围内保持不变,若扩张范围,则数组新增部分的内容是未定义的。
b>分配一个大小为new_size字节的新内存块,并复制大小等于新旧大小中较小者的内存区域,然后释放旧内存块。
若无足够内存,则不释放旧内存块,并返回空指针。
若ptr是NULL,则行为与调用malloc(new_size)相同。
若new_size为零,则行为是实现定义的(可返回空指针,此情况下可能或可能不释放旧内存,或返回不会用于访问存储的非空指针)。
realloc是线程安全的:它表现得如同只访问通过其参数可见的内存区域,而非任何静态存储。
先前令free或realloc归还一块内存区域的调用,同步于任何分配函数的调用,包括分配相同或部分相同内存区域的realloc。这种同步出现于任何分配函数所做的内存访问后,任何realloc所做内存访问前,所有操作一块特定内存区域的分配及解分配函数拥有单独全序。
参数:
ptr:指向需要重新分配的内存区域的指针
new_size:数组新的大小(字节数)
返回值
成功时,返回指向新分配内存的指针。返回的指针必须用free()或realloc()归还。原指针ptr被非法化,而且任何通过它的访问时未定义行为(即便重分配是就地的)。
失败时,返回空指针。原指针ptr保持有效,并需要通过free()或realloc()归还。
第一种情况:后续未分配内存空间足够大,可以分配空间。
int main()
{
int n=5,m=10;
int *ip=(int*)malloc(sizeof(int)*n);//20
if(NULL=ip) return 1;
for(int i=0i<n;i++)
{
ip[i]=i;
}
//...
ip=(int*)realloc(ip,sizeof(int)*m);//40
if(NULL==ip) return 1;
//...
free(ip);
ip=NULL:
}
第二种情况:后续未分配内存空间不足够大,不能分配空间
void *my_realloc(void* p,size_t old_sz,size_t nes_sz)
{
void *vp=malloc(new_sz);
if(vp==NULL) return NULL;
if(p!=NULL)
{
memmove(vp,p,old_sz);
}
return vp;
}
第三种情况:堆内存不足,扩展空间失败,函数返回NULL
int main()
{
int n=5,m=10;
int* ip=(int*)malloc(sizeof(int)*n);
if(ip==NULL) return 1;
for(int i=0;i<n;i++)
{
ip[i]=i;
}
ip=(int*)realloc(ip,sizeof(int)*m);
int *newdata=(int*)realloc(ip,sizeof(int)*m);
if(newdata==NULL)
{
free(ip);
return 1;
}
ip=newdata;
return 0;
}
3.动态管理函数与字符串
void GetStr(char *&p,int n)
{
*p=(char*)malloc(sizeof(char)*n);
if(*p==NULL) exit(EXIT_FAILURE);
}
char* GetString(int n)
{
return (char*)malloc(sizeof(char)*n);
}
void GetString(char **p,int n)
{
*p=(char*)malloc(sizeof(char)*n);
if(*p==NULL) exit(EXIT_FAILURE);
}
int main()
{
int n=100;
char *cp=NULL;
//GetString(&cp,n);
strcpy(cp,"hello tulun");
printf("%s",cp);
pritnf(cp);
free(cp);
return 0;
}
&:【c++中】
取地址;位于;引用【标识符都是从右向左解释】
int main()
{
int a=10;
int b=a;
int &c=a;
}
4.堆区与栈区的区别
1)管理方式:栈由系统自动管理;堆由程序院控制,使用方便,但是易产生内存泄漏。
2)生长方式:栈由低地址扩展(即“向下生长”),是连续的内存区域;堆向高地址扩展(即“向上生长”),时不连续的内存区域。这是由于堆区管理系统用链表来存储空闲内存地址,自然不连续,二链表从低地址向高地址遍历。
3)空间大小:栈顶地址和栈的最大容量由系统预先规定(通常默认1M或10M);堆的大小则受限于计算机系统中有效的虚拟内存,32为Linux系统中堆内存可达2.9G空间。
4)存储内容:栈在函数调用时,首先压入是函数实参,然后主调函数中下条指令(函数调用语句的下条可执行语句)的地址压入,最后是被调函数的局部变量。本次调用结束后,局部变量先出栈,指令地址出栈,最后栈平衡,程序由该点继续下条可执行语句。堆通常在头部用一个字节存放其大小,堆用于存储生存期于函数调用无关的数据,具体内容由程序员安排。
5)分配方式:栈课静态分配或动态分配。静态分配由编译器完成,如局部变量的分配。动态分配由alloca函数在栈上申请空间,用完后自动释放不需要调动free函数。堆只能动态分配且手工释放。
6)分配效率:栈由计算机底层提供支持:分配专门的寄存器存放栈地址,压栈出栈由专门的指令执行,因此效率较高。堆由函数库提供,机制复杂,效率比栈低得多。
7)分配后系统响应:只要栈残余空间大于申请空间,系统将为程序提供内存,否则报告异常提示栈溢出。
操作系统为堆维护一个记录空闲内存地址的链表。当系统收到程序的内存分配申请时,会遍历该链表寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点空间分配给程序。若无足够大小的空间(可能由于内存碎片太多),有可能调用系统功能去增加程序数据段的内存空间,一边有机会分到足够大小的内存,然后进行返回,大多数系统会在该内存空间首地址处记录本次分配的内存大小,供后续的释放函数(如free/delete)正确释放本内存空间。
8)碎片问题:栈不会存在碎片问题,因为栈是先进后出的队列,内存块弹出栈之前,在其上面的后进的栈内容已弹出。二频繁申请释放操作会造成堆内存空间的不连续,从而造成大量碎片,使程序效率降低。
可见,堆容易造成内存碎片;由于没有专门的系统支持,效率很低;由于可能引发用户态和内核态切换,内存申请的代价更为昂贵。所以栈在程序中应用最广泛,函数调用也利用栈来完成,调用过程中的参数、返回地址。栈基指针和局部变量等都采用栈的方式存放。所以,建议尽量使用栈,仅在分配大量或大块内存空间时使用堆。
最后使用栈和堆应避免越界发生,否则可能程序崩溃或破坏程序堆、栈结构,产生意想不到的后果。
5.动态内存管理与结构体
结构体变量和内置类型 都有局部,全局,动态生存期。
struct Student
{
char s_name[10];
int s_age;
};
struct Student s1;
int mian()
{
struct Student s2;
struct Student* sp=(struct Student*)malloc(sizeof(struct Student));
sp->age=100;
free(sp);
sp=nullptr;
return 0;
}
int max=10;
int main()
{
int a=10;
int* ip=(int*)malloc(sizeof(int));
*ip=100;
free(ip);
ip=nullptr;
return 0;
}
struct Student
{
char s_name[10];
int s_age;
float score;
};
struct Student g_studa;//全局
struct Student g_studb={"akashi",18.150};
int main()
{
struct Student studa;//局部未初始化
struct Student studb={"akashi",14,140.5};
struct Student* sp1=(struct Student*)malloc(sizeod(struct Student));//
struct Student* sp2=(struct Student*)malloc(sizeod(*sp2));//
struct Student* sp3=(struct Student*)malloc(sizeod(sp3));//error
strcpy(sp1->s_name,"akashi");
sp1->age=11;
sp1->score=144.5;
free(sp1);
free(sp2);
free(sp3);
}
6.结构体也可以嵌套指向自身的指针
struct Student
{
char s_name[10];//姓名
int s_age; //年龄
float score; //成绩
struct Student* next;//指向Student类型的指针变量属性
};
我们可以一个个的动态申请struct Student类型的内存空间,然后用next指针把他们链接起来。
7.柔性数组
struct sd_node
{
int num;
int size;
char data[];
};
//或
struct sd_node
{
int num;
int size;
char data[0];
};
数组的大小声明为0,或者不给出大小,称之为柔性数组。
注意:全局数组和局部数组不能这样定义。
柔性数组是一种数组大小待定的数组。
在C语言中,可以使用结构体产生的柔性数组,结构体的最后一个元素可以是大小未知的数组。
struct StrNode
{
int len;
int size;
char data[];
};
int main()
{
struct StrNode* sp=nullptr;
int n=0;
scanf_s("%d",&n);
sp=(struct StrNode*)malloc(sizeof(struct StrNode)+sizeof(char)*n);//28
strcpy_s(sp->data,n,"china");
sp->len=5;
sp->size=20;
return 0;
}
8.二位数组
int main()
{
int **ip=nullptr;
int row,col;
scanf("%d %d",&row,&col);//3 5
ip = (int **)malloc(sizeof(int *) *row);
int ar[row][col]={};
for(int i = 0; i < row; i ++)
{
ip[i] = ar[i];//将二维数组行地址赋值到对应的一维指针上。
}
return 0;
}