动态内存分配
为什么存在,静态的通讯录(出现的警告提示)
- 栈上开辟的空间,不能过大。
关于动态内存函数:malloc(sizeof(int)、free(ps)、calloc(10, sizeof(int))、realloc(ps,20*sizeof(int))
内存空间
- 动态内存分配的内存存储在堆区。
使用的函数
malloc()函数、alloc()、realloc()
int main()
{
//假设开辟10个整形的空间 - 10* sizeof(int)
int arr[10];//栈区
//动态内存开辟
//void* p = malloc(10*sizeof(int));
int* p = (int*)malloc(10*sizeof(int));//将void* 类型转换成int*类型。
//使用这些空间的时候
if(p == NULL)
{
//printf("malloc error\n");
perror("main");//错误信息打印,main: xxxxxxxxxxx
return 0;
}
//使用
//……
//回收空间
free(p);
p = NULL;
return 0;
}
malloc()
void* malloc(size_t size);
向内存申请一块连续可用的空间,并返回指向这块空间的指针。
- 如果开辟成功,返回一个指向开辟空间的指针。
- 如果不成功,返回一个空指针,因此malloc()的返回值一定要做检查。
- 返回的是一个void*类型的指针类型,具体类型需要自己定义。
- 如果参数size为0,malloc()的行为是标准未定义的,取决于编译器。
free();
- 如果参数ptr指向的空间不是动态开辟的,那free函数的行为是未定义的。
- 如果参数ptr是NULL指针,则函数什么事情都不做。
- alloc()
int main()
{
int* p = (int*)calloc(10, sizeof(int));//最好加上强制类型转换。
if (p == NULL)
{
return 1;
}
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d\n", *(p + i));
}
free(p);
p = NULL;
return 0;
}
alloc()函数,和malloc()函数主要有两个区别:
void* calloc(size_t number,size_t size)
第一个:参数的不同,第二个会全部初始化成0;
realloc()
- 使动态内存管理更加灵活。
- 有时候会发现申请的空间太小了,或者说太大了。
int main()
{
int* p = (int*)calloc(10, sizeof(int));//10个int的空间。
if (p == NULL)
{
perror("main");
return 1;
}
//使用
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = 5;
}
//这里需要p指向的空间更大,需要20个int的空间。
//realloc()调整空间
int* ptr = (int*)realloc(p, 20 * sizeof(int));
if (ptr != NULL)
{
p = ptr;
}
for (i = 0; i < 20; i++)
{
*(p + i) = 5;
}
free(p);
p = NULL;
return 0;
}
realloc()函数动态内存分配的时候,会出现3种情况,一种找不到空间,一种找到了,但需要重新分配空间if条件判断中的
p = ptr
,还有一种是,在原来动态内存分配空间的基础上,向后开辟空间。是两种开辟空间的时候的返回类型。1.原有空间之后有足够大的空间。2. 没有足够大的空间。
- realloc() - 功能类似于malloc(),就是直接在堆区开辟40个字节。
int main()
{
int* p = (int*)realloc(NULL, 40);
//int* p1 = (int*)malloc(10*sizeof(int));
for (int i = 0; i < 10; i++)
{
*(p + i) = 0;
}
return 0;
}
相较于动态内存分配管理,数组这种数据结构就是静态的。
动态内存出现的常见错误类型
- 对NULL指着的解引用操作。
int main()
{
int* p = (int*)malloc(10*sizeof(int));
//int* p = (int*)malloc(1000000000*sizeof(int));
//可能找不到开辟的空间。
if(p ==NULL)
{
return 1;
}//解决方法
return 0;
}
- 对动态开辟空间的越界访问
int main()
{
int* p = (int*)malloc(10*sizeof(int));
if(p == NULL)
{
return 1;
}
int i = 0;
for(i= 0;i<20;i++)
{
*(p+i) = 5;
}//越界访问内存,只开辟了10个int的空间大小。
return 0;
}
- 使用free()释放非动态开辟的空间
int main()
{
int arr[10] = {0};
int* pa = arr;
free(pa);
pa = NULL;
//pa不是动态开辟的内存。
return 0;
}
- 使用free()释放动态内存中的一部分。
int main()
{
int* p = (int*)malloc(10*sizeof(int));
if(p == NULL)
{
return 1;
}
int i = 0;
for(i = 0;i<5;i++)
{
*p++ = 5;
}
free(p);
p = NULL;
//p指向的位置变了。
return 0;
}
- 对同一块动态开辟的空间,多次释放
int main()
{
int* p = (int*)malloc(10*sizeof(int));
if( p == NULL)
{
return 1;
}
free(p);
//p = NULL;
free(p);
return 0;
}
- 动态开辟的空间忘记释放 - 内存泄漏 - 比较
void test()
{
int* p = (int*)malloc(10*sizeof(int));
if( p == NULL)
{
return;
}
//执行完test()后,p的空间会被销毁,
//但malloc()开辟的空间不会被销毁。
//需要free()
free(p);
p = NULL;
}
int main()
{
test();
return 0;
}
动态开辟的空间,2种回收方式。
- 主动free
- 程序结束 - - 就是main()函数执行完 。服务器环境中,不释放,内存泄漏。
柔性数组
C99语法中规定的,结构中的最后一个元素,是未知大小的数组。
struct S
{
int n;
int arr[];//大小未知。
};
//||
struct s
{
int n;
int arr[0];//大小未知。
};
int main()
{
struct S s={0};
printf("%d\n",sizeof(s));//4
return 0;
}
结构中的 柔性数组中至少要包含一个其他成员。
sizeof返回的柔性数组大小不包含柔性数组的内存。 上面的代码。
包含柔性数组成员的结构用malloc()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适用柔性数组的期望大小。
- 柔性数组的使用
struct S
{
int a;
int arr[0];
};
int main()
{
//使用
struct S* ps = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int));
ps->a = 10;
int i = 0;
for (i = 0; i < 10; i++)
{
ps->arr[i] = i;
}
//扩容
struct S* ptr = (struct S*)realloc(ps,sizeof(struct S) + 20*sizeof(int));
if (ptr != NULL)
{
ps = ptr;
}
//释放
free(ps);
ps = NULL;
return 0;
}
- 不使用柔性数组
struct S
{
int n;
int* arr;
};
int main()
{
struct S* ps = (struct S*)malloc(sizeof(struct S)); //为什么不是sizeof(strucnt S)
if (ps == NULL)
return 1;
ps->n = 10;
ps->arr= (int*)malloc(10 * sizeof(int));
if (ps->arr == NULL)
return 1;
int i = 0;
for (i = 0; i < 10; i++)
{
ps->arr[i] = i;
}
//扩容
int* ptr = realloc(ps ->arr,20 * sizeof(int));
if (ptr != NULL)
{
ps->arr = ptr;
}
//使用
//释放
free(ps->arr);
ps->arr = NULL;
free(ps);
ps = NULL;
return 0;
}
概念:内存碎片、内存池;
方法1:
1.方便内存释放 2.有利于提升访问速度