目录
- [一、 Why要有动态内存的开辟?](#一、 Why要有动态内存的开辟?)
- 简单讨论一下分区
- 二、malloc
- 三、free
- 四、calloc
- 五、realloc
- 六、常见的6大动态内存错误
- 七、经典笔试4道题分析
既然选择远方,当不负青春,砥砺前行
一、 Why要有动态内存的开辟?
在此之前我们已经学会了静态的内存开辟。
c
#include<stdio.h>
int main()
{
int a = 10;
int arr[10] = { 1,2,3,4,5,6 };
return 0;
}
- 但是静态开辟的内存空间大小是固定的。
- 对于数组,一旦长度确定后,就不能在改变了。
但是在实际的开发中,我们需要的空间事先一开始并不知道,而是在程序运行的时候才知道。 所以引入了动态内存的开辟,让我们自己申请和释放空间。
那么在自己申请和释放空间时就一定会用到一下四个函数,掌握了以下四个函数的使用,也就会自己进行动态内存的管理了!!!
简单讨论一下分区
开辟动态内存是在堆区开辟,使用下面四个函数要包含头文件 #include<stdlib.h>
-
栈区:先进后出,程序一开始先从main()进入,main()入栈,为局部变量,函数调用,形式参数,返回值在栈上分配空间,再调用各种函数入栈,出栈。最后mian()出栈,程序执行完毕。栈是有大小的,无限次递归会造成栈溢出。
-
堆区:自己动态地向内存分配空间的区域。malloc calloc realloc 动态开辟内存函数,free 释放动态内存开辟的函数。如果忘记释放动态分配的内存空间可能会导致内存泄露。
-
静态区:存放静态变量和全局变量的区域,这些变量的作用域为整个main()函数,生命周期和程序的生命周期一样,只有程序结束,这些变量才会被回收。
二、malloc
C语⾔动态内存开辟的函数之一:
c
//声明:
void* malloc (size_t size)
- 返回值类型是void * 自己想开辟啥类型的空间,可以自己强转。
- 参数 size 为所要开辟空间的字节数,如果传入的size=0,一般编译器啥也不会报错。
- 如果开辟成功则返回一个指向开辟好空间的指针
- 如果开辟失败则返回一个NULL指针,所以再开辟空间后一定要检查指针是否为NULL
使用:
c
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
int main()
{
int* p = (int* )malloc(4*5); //动态开辟5个整形的空间
if (p == NULL) //检查指针
{
perror("malloc"); //打印错误信息
}
//使用
for (int i = 0; i < 5; i++)
{
p[i] = i + 1;
}
for (int i = 0; i < 5; i++)
{
printf("%d ", p[i]);
}
return 0;
}
- 内存
- 输出
三、free
C语言专门用来做------动态内存的释放和回收。
c
void free (void* ptr)
- 参数ptr指向的空间一定要是动态开辟的,否则报错。ptr 一定指向空间的起始地址
- 如果ptr是NULL,则啥也不干。
c
#include<stdio.h>
#include<stdlib.h>
int main()
{
int* p = (int* )malloc(4*5); //动态开辟5个整形的空间
if (p == NULL)
{
perror("malloc");
}
//使用
for (int i = 0; i < 5; i++)
{
p[i] = i + 1;
}
for (int i = 0; i < 5; i++)
{
printf("%d ", p[i]);
}
free(p);//释放p所指向的动态内存
p = NULL;
//既然释放了,p指向的内存空间也已经被回收了。
//所以p置为NULL更安全
return 0;
}
- 释放的空间不是动态开辟的,报错
四、calloc
calloc 函数也⽤来动态内存分配,那和malloc 有什么不同呢?
c
void* calloc (size_t num, size_t size)
- 参数 num 为开辟空间类型的个数
- 参数 size 未开辟空间类型所占字节的大小
- 与函数 malloc 的区别只在于 calloc 会把申请的空间的每个字节初始化为0
c
#include<stdio.h>
#include<stdlib.h>
int main()
{
int* p = (int* )calloc(5,sizeof(int)); //动态开辟5个整形的空间
if (p == NULL)
{
perror("calloc");
return -1;
}
//输出
for (int i = 0; i < 5; i++)
{
printf("%d ", p[i]);
}
free(p);
p = NULL;
return 0;
}
如果我们所要开辟的空间要进行初始化,可以使用calloc。
五、realloc
realloc函数的出现则让动态内存管理更加灵活!!!
虽然我们可以动态地开辟内存,但是难免会出现,申请的空间过大或者过小的情况。于是realloc 可以再次对动态开辟的空间进行调整。
c
void* realloc (void* ptr, size_t size)
- 参数ptr 指向要调整的内存,ptr 一定指向空间的起始地址
- 参数size 为调整后,新的空间所占字节数
- 返回新开辟的空间地址
这个函数是怎么调整的呢?
在原有内存的基础上,将其拷贝一份到新调整的空间,释放原来的空间,返回新的空间起始地址!
有如下三种情形:
1.原有空间后面有足够大的空间,容得下调整之后的空间大小。
2.原有空间后面不足够大的空间,容不下调整之后的空间大小。
3.调整空间失败。
- 1.原有空间后面有足够大的空间
c
- #define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
int main()
{
int* p = (int* )calloc(5,sizeof(int)); //动态开辟5个整形的空间
if (p == NULL)
{
perror("calloc");
}
//调整p指向的空间,从原来5个int --> 10个int
int* tmp = (int*)realloc(p, sizeof(int) * 10);
if (tmp == NULL)
{
perror("realloc");
return -1;
}
p = tmp;
//使用
for (int i = 0; i < 10; i++)
{
p[i] = i + 1;
}
//打印
for (int i = 0; i < 10; i++)
{
printf("%d ", p[i]);
}
free(p);
p = NULL;
return 0;
}
- 2 原有空间后面不足够大的空间
c
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
int main()
{
int* p = (int* )calloc(5,sizeof(int)); //动态开辟5个整形的空间
if (p == NULL)
{
perror("calloc");
}
//调整p指向的空间,从原来5个int --> 20个int
int* tmp = (int*)realloc(p, sizeof(int) * 20);
if (tmp == NULL)
{
perror("realloc");
return -1;
}
p = tmp;
//使用
for (int i = 0; i < 20; i++)
{
p[i] = i + 1;
}
//打印
for (int i = 0; i < 20; i++)
{
printf("%d ", p[i]);
}
free(p);
p = NULL;
return 0;
}
- 3 调整的空间太大了,调整空间失败。
c
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
int main()
{
int* p = (int* )calloc(5,sizeof(int)); //动态开辟5个整形的空间
if (p == NULL)
{
perror("calloc");
}
//调整p指向的空间,从原来5个int --> #define LLONG_MAX 9223372036854775807i64
int* tmp = (int*)realloc(p, sizeof(int) * LLONG_MAX);
if (tmp == NULL)
{
perror("realloc");
return -1;
}
p = tmp;
//使用
for (int i = 0; i < 20; i++)
{
p[i] = i + 1;
}
//打印
for (int i = 0; i < 20; i++)
{
printf("%d ", p[i]);
}
free(p);
p = NULL;
return 0;
}
六、常见的6大动态内存错误
1.对NULL指针的解引用操作
c
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
void test()
{
int* p = (int*)malloc(INT_MAX);
*p = 20;//如果p的值是NULL,就会有问题
free(p);
}
int main()
{
test();
return 0;
}
2.对非动态内存的空间进行释放
c
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
void test()
{
int a = 6;
int* p = &a;//对非动态内存的空间进行释放
free(p);
}
int main()
{
test();
return 0;
}
3.对动态开辟的空间进行越界访问
c
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
void test()
{
int* p = (int*)calloc(10,sizeof(int));
for (int i = 0; i <= 10; i++)
{
printf("%d ", p[i]); //对动态开辟的空间进行越界访问
}
free(p);
}
int main()
{
test();
return 0;
}
4.动态开辟空间后,未释放,造成空间泄露
c
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
void test()
{
int* p = (int*)calloc(10,sizeof(int));
for (int i = 0; i < 10; i++)
{
printf("%d ", p[i]);
}
//动态开辟空间后,未释放,造成空间泄露
}
int main()
{
test();
return 0;
}
5.对一块动态开辟的空间进行多次释放
c
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
void test()
{
int* p = (int*)calloc(10,sizeof(int));
for (int i = 0; i < 10; i++)
{
printf("%d ", p[i]);
}
free(p);
free(p);//对一块动态开辟的空间进行多次释放
}
int main()
{
test();
return 0;
}
6.使用free释放一块动态开辟内存的一部分
c
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
void test()
{
int* p = (int*)calloc(10,sizeof(int));
p++; //使用free释放一块动态开辟内存的一部分
free(p);
}
int main()
{
test();
return 0;
}
七、经典笔试4道题分析
1.笔试题一
c
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
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;
}
2.笔试题二
c
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
char* GetMemory(void)
{
char p[] = "hello world";
return p;
}
void test(void)
{
char* str = NULL;
str = GetMemory();
printf(str);
}
int main()
{
test();
return 0;
}
3.笔试题三
c
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
void GetMemory(char** p, int num)
{
*p = (char*)malloc(num);
}
void test(void)
{
char* str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str); //就是用完要及时free
}
int main()
{
test();
return 0;
}
4.笔试题四
c
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
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;
}