一、动态内存函数
malloc&free
void *malloc( size_t size );
函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。
- 需要头文件<stdlib.h>,size表示需要开辟内存空间的大小(字节)
- 开辟成功则返回指向这个空间的指针,类型为void*,需要转换为相应类型
int main() { int* i=(int*)malloc(40); if (i == NULL) { printf("%s\n", strerror(errno)); return; } return 0; }
- 开辟失败则返回空指针NULL,因此在使用malloc的返回值之前需要先检查
void free( void *memblock );
free函数用来释放动态开辟的内存。
- 如果参数指向的空间不是动态开辟的,则会 报错
- 如果参数为NULL,则什么都不做
free(i); i = NULL;
calloc
功能与malloc相似,但是calloc的参数不同,同时calloc会将其开辟的空间初始化为0
void *calloc( size_t num, size_t size );
- 功能:为num个大小为size的元素开辟空间,并将空间的每个字节初始化为0
realloc
void *realloc( void *memblock, size_t size );
- 功能:将memblock所指向的内存地址调整成为size个字节的空间,并返回新空间的内存起始位置
- 开辟新空间的几种情况:
1.当原有空间之后有足够大的连续空间,则原有数据不变,直接在原有空间后追加空间
2.当原有空间之后没有足够大的连续空间,则会在堆区寻找另一个合适大小的连续空间使用,并将原有空间的数据拷贝到新空间,再释放原有空间,最后返回新空间的地址
3.在开辟新空间时,同样可能开辟失败,此时会返回NULL,在使用之前需要先检查
int main() { int* i=(int*)malloc(40); int*ptr = (int*)realloc(i, 80);//创建临时指针接收返回值 if (ptr == NULL)//判断是否返回NULL { i = ptr;//不为NULL则将返回值给i } return 0; }
常见动态内存错误
1.对NULL指针的解引用操作
int main() { int* i = (int*)malloc(40); *i = 9;//如果malloc返回的是空指针,则会报错 }
2.对动态开辟空间的越界访问
int main() { int* p = (int*)malloc(40); if(p==NULL) return 1; else { for(int j=0;j<=10;j++) { *(p+j)=j; //当j=10时,会造成越界访问 } } }
3.对非动态开辟内存使用free释放
int main() { int i=10; free(&i);//free只能释放动态开辟的内存 return 0; }
4.使用free释放一块动态开辟内存的一部分
int main() { int* p = (int*)malloc(40); if(p==NULL) return 1; else { for(int j=0;j<=5;j++) { *p=j; p++; } } free(p);//只释放了动态开辟的部分内存空间 ×× return 0; }
5.对同一块动态内存多次释放
int main() { int* i = (int*)malloc(40); //... free(p); //在释放空间后,可加上 p=NULL; 多次释放也不会报错 //... free(p);//多次对p进行释放××× }
6.动态开辟内存忘记释放(内存泄漏)
void emo() { int* i=(int*)malloc(40); //忘记释放动态内存 } int main() { emo(); return 0; }
二、柔性数组
C99 中,结构中的最后一个元素允许是未知大小的数组,这就叫做『柔性数组』成员。这个数组前至少要有一个成员
例如:
typedef struct stu
{
int i;
int arr[0];//柔性数组成员
//或者写成 int arr[];
}Stu;
2.1柔性数组特点
- 要求:在柔性数组成员前必须至少要一个其他成员
- sizeof返回的这种结构不会包括柔性数组的内存
- 内存分配:使用动态内存函数malloc等进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小
2.2柔性数组的使用
int main() { Stu* s1 = (Stu*)malloc(sizeof(struct stu) + 40); //开辟空间的大小等于 结构体其他成员所占内存+柔性数组所要求内存 if (s1 == NULL) return 1; s1->i = 100; for (int j = 0; j < 10; j++) { s1->arr[j] = j; } for (int j = 0; j < 10; j++) { printf("%d ", s1->arr[j]); } //扩容 Stu* ptr = (Stu*)realloc(s1, sizeof(Stu) + 80); if (ptr == NULL) return 1; s1 = ptr; free(s1); s1 = NULL; }
2.3柔性数组的优势
typedef struct stu { int i; int* arr; }Stu; int main() { //将结构体放在堆区 Stu* s1 = (Stu*)malloc(sizeof(struct stu)); //判断是否为NULL省去 s1->i = 100; s1->arr = (int*)malloc(40); for (int j = 0; j < 10; j++) { s1->arr[j] = j; } for (int j = 0; j < 10; j++) { printf("%d ", s1->arr[j]); } //扩容 Stu* ptr = (Stu*)realloc(s1->arr, 80); //判断NULL省去 s1->arr = ptr; //释放 free(s1->arr); free(s1);//这里在释放s1后就清空了s1所指向的空间的成员 //包括arr,因此不用将arr赋成NULL s1 = NULL; }
- 相比于指针,柔性数组只需要一次就能把所有内存释放掉,方便内存释放
- 连续的内存有益于提高访问速度,也有益于减少内存碎片