感谢各位大佬的光临,希望和大家一起进步,望得到你的三连,互三支持,一起进步
个人主页:LaNzikinh-CSDN博客
前言
我们之前讲解了,内存函数和字符串函数的一些知识,我们现在来说一下,关于动态开辟内存的一些函数和一些事例,为什么要开辟动太内存的分布呢?因为我们之前学的都是静态的,有一些题目,你并不知道我需要的内存大小是多少,所以我们就需要了解一个动态开辟的方法。
先从几个动态函数说起
一.malloc函数和free
void* malloc (size_t size);
这个函数就是像内存申请一块连续可用的空间,并且返回指向这块空间的指针,所以我们运用这个函数的时候必须要用一个指针去接收他,如果开辟成功,他就会返回开辟好的指针,如果开辟失败,就会返回一个空指针,因此它的返回值一定要做检查。来看一段代码
#include<stdio.h>
#include<stdlib.h>//注意头文件
#include<assert.h>
int main()
{
int* p = (int*)malloc(sizeof(int) * 10);
assert(p);
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = i;
}
//必须释放动态开辟空间的起始位置
free(p);
p = NULL;
}
free函数就是用来释放空间的,如果动态开辟的内存没有释放的话,那么就会造成很严重的内存泄露问题。
二.calloc函数
void* calloc (size_t num//开辟的元素个数, size_t size//元素的大小);
这两个函数实际上是一样的,不管是他的初始化方式,还是他的功能都是与malloc函数是一样的,但是唯一不同的是malloc函数开辟的是随机值calloc函数开辟的空间会全部初始化把它们存放为0
int main()
{
int* p = (int*)calloc(10, sizeof(int));
assert(p);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
free(p);
p = NULL;
return 0;
}
三.relloc函数(调整)
void* realloc (void* ptr//调整空间的起始位置, size_t size//空间的大小);
调整到底是什么意思呢?就是说如果我之前用malloc函数开辟的空间大小,如果我不满意的话。我就可以用这个relloc函数来调整之前的空间大小。
在调整内存空间是存在两种情况的
情况一:原有空间之后,有足够大的空间
有空间的话,就会直接开辟
情况二:原有空间之后,没有足够大的空间
若后面没有,则会重新开辟,先把原来拷贝进新的,在释放空间
特殊情况:开辟空间失败会自动返回空指针
四.常见的动态内存的错误分析
4.1对NULL指针的解引用操作
void test()
{
int* p = (int*)malloc(INT_MAX);
//如果p的值是NULL,就会有问题
assert(p);
*p = 20;
free(p);
}
4.2 对动态开辟空间的越界访问
void test()
{
int i = 0;
int* p = (int*)malloc(10 * sizeof(int));
assert(p);
//注意这里是<=
for (i = 0; i <= 10; i++)
{
//当i是10的时候越界访问
*(p + i) = i;
}
free(p);
}
4.3 对非动态开辟内存使用free释放
这个就不用代码举例了,记住free函数,只对动态开辟内存的函数使用
4.4 使用free释放一块动态开辟内存的一部分
这个问题的表述可能不清晰,他实际上的意思是说因为free函数他是释放的malloc函数,开辟的初始位置的那个指针,如果释放的不是动态内存的起始位置就会有这个错误
void test()
{
int* p = (int*)malloc(100);
p++;
free(p);//p不再指向动态内存的起始位置
}
4.5 对同一块动态内存多次释放
void test()
{
int* p = (int*)malloc(100);
free(p);
free(p);//重复释放
}
4.6动态开辟内存忘记释放(内存泄漏)
这个也是之前最开始说的最经典的错误就是你开辟了空间,忘记是否就会造成内存泄漏,这个地方也不多说了
五.经典笔试题
5.1能否打印出hello,world
char* GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char* str = NULL;
str = GetMemory();
printf(str);
}
等GetMemory函数返回后使用的指针去访问数组就是非法访问,因为数组的内存已经还给了操作系统,所以不可以。
5.2能否打印
void GetMemory(char *p)
{
p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}
他犯了很多错误,第一个错误就是开辟的空间没有释放,第二个错误就是他其实并没有开辟到空间就是空间开辟了,但我无法获取他开辟的空间,我并没有拿到地址,因为局部变量出去了就会被销毁
改1:利用二级指针二级指针是存放指针变量的地址的,这个题的思路,其实就是我们要把P的地址放进str就可以了
void GetMemory(char **p)
{
*p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(&str);
strcpy(str, "hello world");
printf(str);
free(p);
p=NULL;
}
改2:这个就是利用函数返回值的性质去改的,其实改这个笔试题的思路,就是我必须要把开辟出来的空间的地址找到,所以我直接接触他的返回值也是可以的
void GetMemory(char*p)
{
p = (char *)malloc(100);
return p;
}
void Test(void)
{
char *str = NULL;
str=GetMemory(str);
strcpy(str, "hello world");
printf(str);
free(p);
p=NULL;
}
总结
C语言引用了动态开辟让程序员可以自己申请空间,使代码运行变得比较灵活了。在以后学习数据结构的时候,需要大量运用动态开辟的知识。所以说还是比较重要的。