学习专栏:
《零基础学C语言》
《数据结构世界》
俗话说的好,要想学好数据结构(数据结构世界,对数据结构感兴趣的小伙伴可以移步),就必须学好以下三方面知识:
前两方面的知识在往期已经详细讲解,今天我们就来学习最后一方面的知识——动态内存管理
我们已经掌握的内存开辟方式有:
int val = 20;//在栈空间上开辟四个字节
char arr[10] = {0};//在栈空间上开辟10个字节的连续空间
但是上述的开辟空间的方式有两个特点:
所以,为了能在程序运行中,根据需求灵活地调整空间大小,C语言引入了动态内存管理。
C语言提供了一个动态内存开辟的函数:
void* malloc (size_t size);
这个函数可以开辟一块连续的内存空间,并返回指向这块空间的指针
int main()
{
//动态开辟10个整型空间
int* a = (int*)malloc(10 * sizeof(int));
if (a == NULL)//开辟不成功,返回NULL
{
perror("malloc fail");
return 1;
}
//开辟成功,使用该空间
//...
return 0;
}
注意:malloc(0)——开辟0个字节空间,是标准未定义行为,具体取决于编译器
C语言提供了另外一个函数free,可以动态内存的释放和回收:
void free (void* ptr);
free函数用来释放动态开辟的内存。
int main()
{
//动态开辟10个整型空间
int* a = (int*)malloc(10 * sizeof(int));
if (a == NULL)//开辟不成功,返回NULL
{
perror("malloc fail");
return 1;
}
//开辟成功,使用该空间
//使用完毕,释放该空间
free(a);
a = NULL;
return 0;
}
注意:如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的
C语言还提供了一个函数叫 calloc , calloc 函数也用来动态内存分配。
void* calloc (size_t num, size_t size);
其实,它与malloc很相似,区别在于calloc会将开辟的连续空间,每个字节都初始化为0
int main()
{
//动态开辟10个整型空间并初始化为0
int* a = (int*)calloc(10, sizeof(int));
if (a == NULL)//开辟不成功,返回NULL
{
perror("calloc fail");
return 1;
}
//开辟成功,使用该空间
//使用完毕,释放该空间
free(a);
a = NULL;
return 0;
}
realloc函数的出现让动态内存管理更加灵活。
为了合理的运用内存,所以在开辟空间后觉得太大或太小,就可以使用realloc进行调整。
void* realloc (void* ptr, size_t size);
realloc调整成功,会返回调整后指向这块空间的指针
realloc在调整内存空间的是存在两种情况:
这个时候,realloc就会再找一块足够大空间,一次性开辟完,再将原数据拷贝过去。
int main()
{
//动态开辟10个整型空间
int* a = (int*)malloc(10 * sizeof(int));
if (a == NULL)//开辟不成功,返回NULL
{
perror("malloc fail");
return 1;
}
//调整空间,扩大为20个整型空间
int* tmp = (int*)realloc(a, 20 * sizeof(int));
if (tmp == NULL)//开辟不成功,返回NULL
{
perror("realloc fail");
return 1;
}
//调整成功,将空间地址给a
a = tmp;
//使用空间...
free(a);
a = NULL;
return 0;
}
注意:
void test()
{
int *p = (int *)malloc(INT_MAX/4);
*p = 20;//如果p的值是NULL,就会有问题
free(p);
}
void test()
{
int i = 0;
int *p = (int *)malloc(10*sizeof(int));
if(NULL == p)
{
exit(-1);//终止程序
}
for(i=0; i<=10; i++)
{
*(p+i) = i;//当i是10的时候越界访问
}
free(p);
}
void test()
{
int a = 10;
int *p = &a;
free(p);//ok?
}
void test()
{
int *p = (int *)malloc(100);
p++;
free(p);//p不再指向动态内存的起始位置
}
void test()
{
int *p = (int *)malloc(100);
free(p);
free(p);//重复释放
}
因为程序终止会回收所有空间,所以这里用死循环来模拟程序运行中不释放动态内存,会导致内存泄漏。
int main()
{
int* a = (int*)malloc(10 * sizeof(int));
while(1);
return 0;
}
总结:动态开辟的内存一定要释放,并且要正确释放
请先自行思考哦,不要立马看解析~
请问以下题目运行Test 函数会有什么样的结果?
void GetMemory(char *p)
{
p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}
结果:
解析:
char *GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory();
printf(str);
}
结果:
解析:
void GetMemory(char **p, int num)
{
*p = (char *)malloc(num);
}
void Test(void)
{
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}
结果:
解析:
void Test(void)
{
char *str = (char *) malloc(100);
strcpy(str, "hello");
free(str);
if(str != NULL)
{
strcpy(str, "world");
printf(str);
}
}
结果:
解析:
也许你从来没有听说过柔性数组(flexible array)这个概念,但是它确实是存在的。
C99 中,结构中的最后⼀个元素允许是未知大小的数组,这就叫做『柔性数组』成员。
typedef struct st_type
{
int i;
int a[0];//柔性数组成员
}type_a;
有些编译器会报错无法编译可以改成:
typedef struct st_type
{
int i;
int a[];//柔性数组成员
}type_a;
typedef struct st_type
{
int i;
int a[0];//柔性数组成员
}type_a;
int main()
{
printf("%d\n", sizeof(type_a));//输出的是4
return 0;
}
int main()
{
int i = 0;
type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));
//使用空间
p->i = 100;
for(i=0; i<100; i++)
{
p->a[i] = i;
}
free(p);
return 0;
}
这样柔性数组成员a,相当于获得了100个整型元素的连续空间。
C/C++程序内存分配的几个区域:
看到这里了还不给博主扣个:
⛳️ 点赞☀️收藏 ⭐️ 关注!
❤️
拜托拜托这个真的很重要!
你们的点赞就是博主更新最大的动力!
有问题可以评论或者私信呢秒回哦。