前言:通过前面的学习,我们知道C语言中在内存中开辟空间的方法有:变量和数组。既然拥有了开辟空间的方法,我们为什么还要学习动态内存分配呢?
int val = 20; //在内存中开辟四个字节的空间
int arr[10] = { 0 }; //在内存中开辟四十个字节的连续空间
变量和数组确实可以在内存中开辟空间,但它们也有一些不足的点 :①、开辟的空间大小是固定的,②、数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配。但很多时候我们无法一开始就知道程序到底需要多少内存,使用数值和变量当程序需要的内存变大时,数组和变量开辟的空间是无法变大的,满足不了程序的需求,动态内存正是在这样的基础上诞生。
一、动态内存函数:
1.1malloc和free:
1.1.1mall函数语法和使用:
malloc函数语法:
malloc函数作用:动态开辟内存,可以向内存申请一块连续可以的空间,并返回指向这块空间的指针。
malloc开辟空间指针返回注意点:
如果间开辟成功时,返回指向该空间的指针。
如果开辟空间失败,返回一个NULL指针。
malloc函数的返回值类型为:void* 类型,malloc函数不会指定开辟空间的类型,具体类型由使用者决定。
函数头文件: stdlib.h
C++plus地址:malloc - C++ 参考 (cplusplus.com)
int main()
{
int* p = (int*)malloc(40);
return 0;
}
空间打印演示:
int main()
{
int* p =(int*) malloc(40);
//判断空间是否开辟成功
if (p == NULL)
{
perror("malloc"); //开辟失败报错
return 1;
}
//开辟成功
int i = 0;
for ( i = 0; i < 10; i++)
{
printf("%d\n",*(p+1)); //以十进制打印指针内容
}
return 0;
}
代码里面的 int* p =(int*) malloc(40); 与int arr[10] 具有相同的效果,int类型为4个字节,10个int类型的数据刚好大小为40个字节。
malloc函数开辟内存块的大小,以字节为单位。是无符号整数类型(size_t);
malloc申请的空间在内存中的存储位置:
malloc申请空间后 :
malloc 申请到空间后,直接返回的是这快空间的起始地址,不会初始化该空间的内容。所以是上面的代码打印过程中,我们会发现打印的值为一些不正常的数字。
int main()
{
int* p = (int*)malloc(40);
if (p == NULL)
{
perror("malloc");
return 1;
}
int i = 0;
for ( i = 0; i < 10; i++)
{
*(p+i) = i;
}
for ( i = 0; i < 10; i++)
{
printf("%d ",*(p+i));
}
free(p);
return 0;
}
上面代码中最后在完成打印数据时,书写了free(p); 的语句,当我们将它去掉,在VS中就行打印时,视乎也没有什么特别大的问题,但这里却必须要使用它,这是为什么呢?
1.1.2 free函数:
free函数语法:
void free (void* ptr);
free函数的作用:动态内存的释放和回收。
free函数注意点:
①、如果ptr指向的空间不是动态开辟的,那么函数free的行为就是未定义行为。
②、如果参数ptr是NULL指针,则函数什么事情都不用做。
函数头文件:stdlib.h
C++plus地址:free - C++ 参考 (cplusplus.com)
1.2calloc和realloc函数:
前面我们学习了malloc同态内存开辟,calloc函数的作用也是进行动态内存开辟的.
calloc函数语法:
函数功能:开辟num个大小为size的元素开辟一块空间,并且会将空间的每个字节初始化为0。
int main()
{
int* p = (int*)calloc(10, sizeof(int)); //calloc动态空间开辟,大小40字节
if (p == NULL)
{
perror("calloc"); //错误检测
}
//打印数据
int i = 0;
for ( i = 0; i < 10; i++)
{
printf("%d ",p[i]);
}
free(p);
return 0;
}
realloc函数语法:
ptr 是要调整的内存地址,size调整之后新大小。
realloc的返回值为调整后的内存起始位置。
realloc函数不仅会调整原内存空间大小,还会将原来内存中的数据移动到新的空间。
realloc函数调整内存空间的两种情况:
①、原有空间之后有足够大的空间。
直接在原有内存之后追加空间,原空间的数据不变。
②、原有空间之后没有足够大的空间。
第二种情况时,会开辟新的空间,并且将就的空间中的数据拷贝到新的空间中,释放旧的空间,并且返回新空间的地址。
函数使用代码:
int main()
{
int* p = (int*)malloc(40);
if (p == NULL)
{
perror("malloc");
return 1;
}
//初始化为1~10
int i = 0;
for ( i = 0; i < 10; i++)
{
p[i] = i + 1;
}
//扩展空间
int* ptr = realloc(p,80);
if (ptr != NULL)
{
p = ptr;
}
return 0;
}
1.3常见的动态内存错误:
1.对NULL指针进行解引用操作:
int main()
{
int* p = (int*)malloc(INT_MAX/4);
*p = 20;
free(p);
}
2、对动态开辟的空间越界访问:
int main()
{
int i = 0;
int* p = (int*)malloc(10*sizeof(int));
if (NULL == p)
{
perror(malloc);
}
for ( i = 0; i <= 10; i++)
{
*(p+i) = i;
}
free(p);
}
3、对非动态开辟内存使用free释放:
int main()
{
int a = 10;
int* p = &a;
free(p);
}
4、同一块空间多次释放:
int main()
{
int i = 0;
int* p = (int*)malloc(10*sizeof(int));
if (NULL == p)
{
perror(malloc);
}
for ( i = 0; i < 10; i++)
{
*(p+i) = i;
}
free(p);
p = NULL;
free(p); //错误地方。
return 0;
}
5、动态内存开辟忘记释放:(内存泄漏)
int main()
{
int* p =(int*) malloc(40);
//判断空间是否开辟成功
if (p == NULL)
{
perror("malloc"); //开辟失败报错
return 1;
}
//开辟成功
int i = 0;
for ( i = 0; i < 10; i++)
{
printf("%d\n",*(p+1)); //以十进制打印指针内容
}
return 0;
}