目录
动态内存函数
动态内存开辟的空间存放在堆区中,需要程序员手动开辟、释放。
所有动态内存函数需包含头文件 <stdlib.h>
malloc
全称:memory allocation
向内存申请一块连续可用的空间(不初始化),返回指向该空间的指针(void* 类型),若申请失败,则返回空指针。
由于可能会申请失败,所以在申请后须判断是否申请成功。
由于返回的指针为 void* 类型,所以须将该指针转换为对应类型再进行接收。
用法:
int* a = NULL;//接收申请空间的指针
a = (int*)malloc(40);向内存申请40字节空间并转换成对应类型
if (a == NULL)//判断是否申请失败
{
printf("%s\n", strerror(errno));//打印错误信息
return;//结束该函数
}
calloc
全称:clear allocation
向内存申请一块连续可用的空间(并初始化),返回指向该空间的指针(void* 类型),若申请失败,则返回空指针。
用法:
int* a = NULL;//接收申请空间的指针
a = (int*)calloc(4,10);向内存申请4个元素的空间,每个元素10字节,共40字节
if (a == NULL)//判断是否申请失败
{
printf("%s\n", strerror(errno));//打印错误信息
return;//结束该函数
}
realloc
全称:reset allocation
对申请的空间调整大小(不初始化),若调整成功,则返回指向调整后空间的指针(void* 类型),若调整失败,则返回空指针。
由于可能会调整失败,所以在调整时须创建中间变量来接收并判断。
用法:
int* a = NULL;
a = (int*)malloc(40);
if (!a)
{
printf("%s\n", strerror(errno));
return;
}
int* p = realloc(a, 10);//将a指向的空间调整为10字节并赋给p
if (p != NULL)
a = p;//如果p不为空指针则才赋给a
对空间进行增容分为两种情况:
(1)原空间后有足够增容空间,则直接在原空间后进行增容,并返回指向原空间的指针。
(2)原空间后无足够增容空间,则开辟新空间,将原有数据进行拷贝,并返回指向新空间的指针。
对空间进行减容则返回指向原空间的指针。
调整空间时可能会出现少量空间无法利用到的情况,产生内存碎片。
free
对申请的空间进行释放
当动态内存使用完毕后,须对该空间进行释放还给系统,否则会发生内存泄漏。
内存泄漏:动态开辟的空间使用完毕后,由于某种原因未释放或无法释放,导致程序运行变慢或崩溃。
由于释放空间后指针还是指向该空间位置,为了避免非法访问,须将指向该空间的指针置空。
用法:
int* p = (int*)malloc(40);
free(p);//释放p指向的空间
p = NULL;//将p置空
练习(判断下列代码运行结果)
(1)
void GetMemory(char *p)
{
p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}
int main()
{
Test();
return 0;
}
答案:内存泄漏;程序崩溃
未释放内存导致内存泄漏;指针传参时,传递的时自己指向的地址,因为 str 指向 NULL,所以传给 p 后,p 也指向了 NULL,不会改变 str 的指向,当对 str 进行 strcpy 时,由于 str 为空指针,不可进行修改,导致程序崩溃。
(2)
char *GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory();
printf(str);
}
int main()
{
Test();
return 0;
}
答案:内存泄漏;打印正常或随机值
未释放内存导致内存泄漏;局部变量 p 出了函数后被回收,但函数返回了 p 的地址,根据地址可以找到该空间,由于空间回收后不会被初始化,还保留原有值,若该空间还未被使用,则正常打印,若被使用,则打印随机值。
(3)
void GetMemory(char **p, int num)
{
*p = (char *)malloc(num);
}
void Test(void)
{
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}
int main()
{
Test();
return 0;
}
答案:内存泄漏;正常打印
未释放内存导致内存泄漏;str 为指针,所以传入 &str 须用二级指针接收,解引用后找到 str 并将它指向申请的空间,最后进行 strcpy 并打印。
(4)
void Test(void)
{
char *str = (char *) malloc(100);
strcpy(str, "hello");
free(str);
if(str != NULL)
{
strcpy(str, "world");
printf(str);
}
}
int main()
{
Test();
return 0;
}
答案:非法访问;正常打印
free(str)后,str 还指向原来的空间,这时对 str 进行 strcpy,就改变了已经被回收的空间。
柔性数组
结构体内未指定元素个数的数组,且必须是最后一个成员,初始状态下不占用结构体空间。
例:
struct S
{
int a;
int arr[];//柔性数组
}s;
int main()
{
sizeof(s);
return 0;
}
结果:4
用法 :
struct S
{
int a;
int arr[];//柔性数组
}*s;
int main()
{
s = (struct S*)malloc(44);//前4字节为a的空间,剩下为arr的空间
return 0;
}