全局变量——在静态区开辟内存
局部变量——在 栈区开辟内存
内存使用情况:

int val =20; //在栈空间上开辟4个字节
char arr[10]={0}; //在栈空间上开辟10个字节的连续空间空间开辟⼤⼩是固定的。
数组在申明的时候,必须指定数组的⻓度,数组空间⼀旦确定了⼤⼩不能调整 。
所以抛出问题,我们需要的空间⼤⼩在程序运⾏的时候才能知道,那数组的编译时开辟空间的⽅式就不能满⾜了,所以就有了动态内存开辟的概念
malloc和free都声明在 stdlib.h 头⽂件中。
函数的功能: 开辟一个内存块
函数的参数: 开辟空间的字节大小
函数的返回类型: void * 空指针类型

• 如果开辟成功,则返回⼀个指向开辟好空间的指针。
• 如果开辟失败,则返回⼀个 NULL 指针,因此malloc的返回值⼀定要做检查。
• 返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使⽤的时候使⽤者⾃ ⼰来决定。
• 如果参数 size 为0,malloc的⾏为是标准是未定义的,取决于编译器。
free函数是专⻔⽤来做动态内存的释放和回收的,当我们使用malloc函数申请空间不再使用的时候,就应该还给操作系统

• 如果参数 ptr 指向的空间不是动态开辟的,那free函数的⾏为是未定义的。
• 如果参数 ptr 是NULL指针,则函数什么事都不做。
free(p);
p=NULL;例:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int* p = (int*)malloc(40);
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = i;
}
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
free(p);//释放ptr所指向的动态内存
p = NULL;//是否有必要?
return 0;
}我们通过调试发现通过free函数将malloc函数开辟的空间释放后,p依然能找到这块空间,为了避免这块空间的错误使用,我们将p=NULL,赋成空指针,这样就p指针就真正的断开了与这块空间的联系。

函数功能: 开辟一个内存中连续的空间并将空间里的每个字节初始化为0
函数参数 第一个参数:元素的个数 第二个参数 :每个元素的字节大小
函数的返回类型: void * 空指针类型

• 函数的功能是为 num 个⼤⼩为 size 的元素开辟⼀块空间,并且把空间的每个字节初始化为0。
• 与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全
例:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int* p = (int*)calloc(10, sizeof(int));//开辟10个int类型的空间,40个字节
if (NULL != p)
{
int i = 0;
for (i = 0; i < 10; i++) //没有赋值,直接对内容进行打印
{
printf("%d ", *(p + i));
}
}
free(p);
p = NULL;
return 0;
}
当我们开辟一块空间后,发现空间不够用,想要调整更大的空间
函数功能: 重新分配内存空间,调整之前开辟动态内存空间的大小
函数参数: 第一个参数 是之前开辟内存块的地址 ,第二个参数 是调整后空间的字节大小。
函数返回类型: void * 空指针类型

• ptr 是要调整的内存地址
• size 调整之后新⼤⼩
• 返回值为调整之后的内存起始位置。
• 这个函数调整原内存空间⼤⼩的基础上,还会将原来内存中的数据移动到 新 的空间。
realloc调整空间失败,会返回空指针
• realloc在调整内存空间成功存在两种情况:
◦ 情况1:原有空间之后有⾜够⼤的空间
直接进行扩大,返回旧的空间的起始地址◦ 情况2:原有空间之后没有⾜够⼤的空间
realloc函数会在内存堆区重新找一块空间(满足新空间的大小需求),同时会把旧的数据拷贝到新的空间中,然后释放旧的空间,同时返回新空间的起始地址例1:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int* ptr = (int*)malloc(100);
if (ptr != NULL)
{
//业务处理
}
else
{
return 1;
}
//扩展容量
//代码1 - 直接将realloc的返回值放到ptr中
ptr = (int*)realloc(ptr, 1000);//error
//代码2 - 先将realloc函数的返回值放在p中,不为NULL,再放ptr中
int* p = NULL;
p = realloc(ptr, 1000);
if (p != NULL)
{
ptr = p;
}
//业务处理
free(ptr);
return 0;
} ptr = (int*)realloc(ptr, 1000);//如果调整失败,原本开辟的空间也找不到,所以不能直接将realloc的返回值放到ptr中 例2:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int * p = (int *)malloc(10 * sizeof(int));
if (p == NULL)
{
printf("内存开辟空间失败\n");
}
else
{
int i = 0;
for (i = 0; i<10; i++)
{
p[i] = i;
}
for (i = 0; i<10; i++)
{
printf("%d ", p[i]);
}
}
//这里在堆区开辟了一个40字节的空间
//假如内存空间不够用,我们想调整的更大一些,调整成80个字节的空间大小
int* p2 = (int *)realloc(p,20*sizeof(int));
if (p2 == NULL)
{
printf("内存开辟失败\n");
}
else
{
int i = 0;
for (i = 10; i < 20; i++)
{
p[i] = i;
}
for (i = 10; i < 20; i++)
{
printf("%d ", p[i]);
}
}
free(p2);
p2 = NULL;
return 0;
}例3:
int main()
{
int* p = (int*)realloc(NULL,40);//等价于malloc函数
free(p);
p = NULL;
return 0;
}void test()
{
int *p = (int *)malloc(100);
*p = 20;//如果p的值是NULL,就会有问题
free(p);
}#include <stdio.h>
#include <stdlib.h>
int main()
{
int *p = (int *)malloc(40);
if (p == NULL)
{
printf("开辟内存空间失败\n");
}
else
{
int i = 0;
for (i = 0; i<=10; i++)
{
p[i] = i;//当循环到第11次的时候就越界访问了
//*(p + i) = i;
}
}
return 0;
}malloc,calloc,realloc函数申请的空间如果不主动释放,出了作用域是不会销毁的
释放的方式:1.free主动释放 2.程序结束,操作系统回收
free函数只能对我们开辟的动态内存进行释放操作
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a=10;
int *p=&a;//p指向的空间不再是堆栈上的地址
free(p);
p=NULL;
return 0;
}
3.4使用free释放⼀块动态开辟内存的⼀部分
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *p = (int *)malloc(10 * sizeof(int));
if (p == NULL)
{
printf("内存开辟失败\n");
}
p++;
free(p);//释放空间的p不是起始地址
p = NULL;
return 0;
}我们最后free(p)时p指向的不是内存的初始位置,所以我们开辟的这块空间并不能完全释放,会造成内存泄漏。

#include <stdio.h>
int main()
{
int *p = (int *)malloc(100);
free(p);
free(p); //重复释放
p=NULL;
return 0;
}注意:同一块内存空间只能释放一次
#include <stdio.h>
int main()
{
int *p = (int *)malloc(100);
if (NULL != p)
{
*p = 20;
}
while (1);
return 0;
}注意:忘记释放不再使用的动态开辟的空间会造成内存泄漏。一定要正确释放
#include <stdlib.h>
#include <string.h>
void GetMemory(char *p)
{
p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str, "helloworld");
//此处str依然是空指针,对空指针解引用操作符会造成程序崩溃,将 “hello world ”放入空指针指向的空间,最后程序必然会崩溃。
printf(str);
}
int main()
{
Test();
return 0;
}GetMemory(str)把str空指针传到该函数中的p,在函数内部开辟了100个字节空间的大小,p是GetMemory函数的形参,只能在函数内部有效,等GetMemory函数返回之后,动态开辟内存尚未释放并且无法找到,所以会造成内存泄漏
改正:
#include <stdlib.h>
#include <string.h>
void GetMemory(char **p)
{
*p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(&str);
strcpy(str, "hello world");
printf(str);
free(str);
str = NULL;
}
int main()
{
Test();
return 0;
}#include <stdio.h>
#include <stdlib.h>
char* GetMemory(char *p)
{
p = (char *)malloc(100);
return p;//因为p的地址在堆区,所以返回首地址还能找到之前开辟的空间
}
void Test(void)
{
char *str = NULL;
str = GetMemory(str);
strcpy(str, "helloworld");
printf(str);
}
int main()
{
Test();
return 0;
}#include <stdio.h>
#include <stdlib.h>
char *GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory();//str此时是野指针
printf(str);
}
int main()
{
Test();
return 0;
}char p[]是局部变量,出了该函数内存就会还给操作系统,当返回数组首地址并赋值到str上是没有意义的,当我们打印的时候就会出现问题,找不到数组的内容
#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);
}
int main()
{
Test();
return 0;
}忘记释放动态开辟的内存,导致内存泄漏了
改正:
#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(p);
p=NULL;
}
int main()
{
Test();
return 0;
}#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;
}在free释放空间后,没有对str指针置空(NULL),所以操作系统依然记着所开辟空间的地址,但是这块内存已经不属于我们了,,当对该地址再次使用时就造成了非法访问的问题