
--已经掌握的内存开辟方式:
int a = 20;//在栈空间中开辟4个字节
char arr[10] = { 0 };//在占空间开辟10个字节--上述方式特点:
--有时,空间大小只能在程序运行时确定,所以以上方式不适合——引进了动态内存开辟,允许自己申请和释放空间。
--介绍提供的动态内存开辟函数:
void* malloc(size_t size);--介绍:
--包含 stdlib.h头文件 功能:向内存在堆区申请一块连续的空间; 参数:size ,要申请空间的大小(例如:要输入n个 int 型数据,大小为 n * sizeof(int) ); 返回值:指向开辟好空间的指针(地址起始位置);
--注意:
--开辟成功,返回指向开辟好空间的指针; --开辟失败:返回 NULL指针,一定要检查mallco的返回值!; --返回类型void* ,具体类型看需要; --size函数若为0,行为是未定义的。
--专门用来释放动态内存。
void free(void* ptr);--介绍:
--包含 stdlib.h头文件 功能:函数用于释放之前通过 malloc() 等函数动态分配的内存; 参数
ptr:是一个指向先前分配的内存块的指针,指针必须是通过 malloc()等 返回的指针值(起始位置); 返回值:void,没有返回值。
--注意:
--参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的; --若参数 ptr 是NULL指针,函数无事干; --每一个
malloc()等开辟内存函数的调用,必须最终对应一个free()的调用; --禁止重复释放,建议释放后立即置空。
#include<stdlib.h>
int main()
{
int n = 0;
printf("输入申请个数:");
//输入个数
scanf("%d", &n);
//申请空间,存放数据
int* ptr = (int*)malloc(n * sizeof(int));
if (p == NULL)//判断非空
{
perror("malloc");
return 1;
}
//使用申请的内存空间
for (int i = 0;i < n;i++)
{
p[i] = i + 1;
printf("%d ", p[i]);
}
free(ptr);//用完就释放
ptr = NULL;//释放就置空
return 0;
}--补充:
--对于使用malloc函数时:要输入 int 型数据,就用int型指针变量接收;因函数返回类型为void*,需要强转为 int*来使用。
void* calloc(size_t num, size_t size);--介绍:
--包含头文件 <stdlib.h> ; 功能:在内存的堆区动态地分配一块连续的内存空间,并将所有字节初始化为零; 参数:
num --需要分配的元素个数、size --每个元素的大小(以字节为单位); 返回值:分配成功,返回指向这块内存起始地址的void*指针;分配失败(如内存不足),则返回NULL。
--注意:
--检查返回值:和
malloc()一样,检查calloc()的返回值是否为NILL;-- 与malloc()的核心区别:calloc()会进行初始化,而malloc()不会; --需要类型转换。
--演示 malloc 与 calloc函数的区别:
int main()
{
int n = 0;
printf("输入申请个数:");
//输入个数
scanf("%d", &n);
//申请空间存放数据
int* p = (int*)calloc(n, sizeof(int));
if (p == NULL)//判断是否为空
{
perror("malloc");
return 1;
}
//使用内存空间,看是否初始化为0
for (int i = 0;i < n;i++)
{
printf("%d ", p[i]);
}
free(p);//用完就释放
p = NULL;//释放就置空
return 0;
}
输入申请个数:10
输出:0 0 0 0 0 0 0 0 0 0--上面可知:calloc函数“先置零,再使用”,不信任未初始化的内存,主动为自己创造一个安全可控的环境后再开始工作。优势所在!!
void* realloc(void* ptr, size_t size);--介绍:调整原内存空间大小基础上,会将原内存中数据移到新空间。
功能:用于重新分配之前通过
malloc(),calloc(), 或realloc()分配的内存块的大小 -- 扩大或缩小; 参数:ptr --指向先前分配的内存块的指针。如果ptr是NULL,则realloc()的行为等同于malloc(new_size)(开辟空间);size --这是新的内存块大小,以字节为单位。如果size为0,并且ptr不为NULL,那么realloc的行为就等同于free(ptr),并且返回 NULL; 返回值:函数返回一个指向新分配内存的指针。这个指针可能与ptr相同,也可能不同。如果分配失败,则返回NULL,并且原来的内存块不会被释放或修改。
--注意:
--不要直接将返回值赋给原指针:因为若返回NULL就会覆盖原地址,导致旧数据丢失--使用临时指针在赋值;
--realloc函数调整内存空间的情况:
情况1--原有空间后有足够大的空间进行调整:直接进行操作,保留原数据; 情况2--原有空间后没有足够大的空间进行调整:在堆空间寻找合适大小的连续空间,并且返回新地址;
--代码+图示:
int main()
{
//申请一块连续空间,存放数据
int* p = (int*)malloc(5 * sizeof(int));//
//判断是否为空
if (p == NULL)
{
perror("malloc");
return 1;//错误结束
}
//使用空间
//先存放1~5
for (int i = 0; i < 5; i++)
{
p[i] = i + 1;
}
//扩大空间,继续存放6~10
int* p2 = (int*)realloc(p, 10*sizeof(int));
if (p2 == NULL)
{
perror("realloc");
return 1;
}
p = p2;//仍然使用p指向空间地址
//存放
for (int i = 5; i < 10; i++)
{
p[i] = i + 1;
}
//打印空间内的数据
for (int i = 0; i < 10; i++)
{
printf("%d ", p[i]);
}
free(p);//释放空间
p = NULL;//置空,防止指向无效地址成为野指针
return 0;
}x64环境:

x86环境:

--注意在调试时(vs2022),现将p的地址显出来后在添加p2进行显示地址!

int main()
{
int* p = (int*)malloc(INT_MAX / 4);
/*if (p == NULL)
{
return 1;
}*/
//如果p的值是NULL,就会有问题
for (int i = 0; i < 10; i++)
{
p[i] = i + 1;
}
free(p);
p = NULL;
return 0;
}
--注意一定要判断内存开辟函数的返回值!!
int main()
{
//申请空间
int* p = (int*)malloc(5 * sizeof(int));
//判断返回值
if (p == NULL)
{
perror("malloc");
return 1;
}
//使用空间
for (int i = 0; i < 10; i++)
{
p[i] = i + 1;//注意i值,i > 5超过了动态内存空间大小,越界
printf("%d ", p[i]);
}
//释放、置空
free(p);
p = NULL;
return 0;
}
--虽然最后打印出1~10 侥幸成功,但是这样是错误的!
int main()
{
int arr[10] = { 0 }; 数组 栈区的空间 非动态
int* p = arr;
//.....
//使用p
//.....
free(p); 释放动态空间 堆区
p = NULL;
return 0;
}--free函数用于释放之前通过 malloc() 等函数动态分配的内存;对于栈区的内存,会自动销毁!!
int main()
{
//申请一块空间
int* p = (int*)malloc(5 * sizeof(int));
if (p == NULL)//判断
{
perror("malloc");
return 1;
}
//使用空间
for (int i = 0;i < 5;i++)
{
*p = 1 + i;
p++;
}
//循环结束,p指向了5以后的空间
//free释放空间,一定要给空间的起始位置
free(p);
p = NULL;
return 0;
}-- free 函数参数指针必须是通过 malloc()等 返回的指针值(地址起始位置);对于数据存放用 - - p[ i ]= i + 1、*(p+i) = i+1,使用不要使用p++......
int main()
{
int* p = (int*)malloc(10 * sizeof(int));
if (p == NULL)
{
perror("malloc");
return 1;
}
//使用内存空间
//…………
free(p);
//…………
free(p);
p = NULL;
return 0;
}
void test()
{
int* p = (int*)malloc(10 * sizeof(int));
if (p == NULL)
{
perror("malloc");
return 1;
}
//最后没有释放
}
int main()
{
test();
//test() 函数结束时,栈帧被回收,p不存在,丢失内存的地址,内存空间仍然存在
//…………
//…………
return 0;
}--忘记释放不再使用的动态开辟的空间会造成内存泄漏; --注意:动态开辟的空间⼀定要释放,并正确释放。
--在绝大多数情况下,即使忘记 free 当程序正常结束时,操作系统会自动回收动态分配的所有内存。
//错误示例
#include<stdio.h>
#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);
}
int main()
{
Test();
return 0;
}--问题:
--修改:
//修改1
#include<stdio.h>
#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);//释放
}
int main()
{
Test();
return 0;
}//修改2
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
char* 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(str);//释放
}
int main()
{
Test();
return 0;
}//错误示例
#include<stdio.h>
#include<stdlib.h>
#include<string.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;
}--修改:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
char* GetMemory(void)
{
static char p[] = "hello world";//变为静态变量,改变生命周期
return p;
}
void Test(void)
{
char* str = NULL;
str = GetMemory();
printf(str);
}
int main()
{
Test();
return 0;
}--可能注意到,为什么 试题1_修改2 就是retuen形参可以呢?? --因为malloc开辟的动态内存只能被手动释放,函数返回时不会被销毁;而本题数组存在栈区,返回时销毁,且涉及到地址,函数最会返回的是数组首地址,但是原空间不存在,所以错误。
//错误示例
#include<stdio.h>
#include<stdlib.h>
#include<string.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>
#include<string.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(str);//用完就释放
str = NULL;//释放就置空
}
int main()
{
Test();
return 0;
}#include<stdio.h>
#include<stdlib.h>
#include<string.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;
}--修改:
void Test(void)
{
char* str = (char*)malloc(100);
strcpy(str, "hello");
free(str);
str = NULL;//及时置空,防止成为野指针致非法访问
//或者最后在进行释放、置空
if (str != NULL)
{
strcpy(str, "world");
printf(str);
}
}
int main()
{
Test();
return 0;
}--柔性数组:C99中,结构体中最后一个成员可以是未知大小的数组,称为柔性数组成员;可以自由调整大小。
struct st_type
{
int i;
int a[];//柔性数组成员--[]中填0看编译器选择
};int main()
{
struct s
{
int i;//4
int a[];//...
};
printf("%zu\n", sizeof(struct s));
return 0;
}
输出:4 //不包含柔性数组大小--验证了第三条特点。
#include <stdlib.h>
struct s
{
int n;
int arr[];
};
int main()
{
struct s* ps = (struct s*)malloc(sizeof(struct s) + 10 * sizeof(int));
//计算结构体大小-n的大小,后面10*sizeof(int)是为柔性数组申请的空间
if (ps == NULL)
{
perror("malloc");
return 1;
}
//直接使用空间
ps->n = 100;
int i = 0;
for (i = 0; i < 10; i++)
{
ps ->arr[i] = i + 1;
}
//空间不够用,调整
struct s* tmp= (struct s*)realloc(ps, sizeof(struct s) + 20 * sizeof(int));
if (tmp == NULL)
{
perror("realloc");
return 1;
}
ps = tmp;
//继续使用空间
//...
//释放
free(ps);
ps = NULL;
return 0;
}struct s
{
int n;
int* arr;
};
int main()
{
struct s* ps = (struct s*)malloc(sizeof(struct s));
if (ps == NULL)
{
perror("malloc");
return 1;
}
ps->n = 100;
int* ptr = (int*)malloc(10 * sizeof(int));
if (ps == NULL)
{
perror("malloc2");
return 1;
}
ps->arr = ptr;
//存放1~10
int i = 0;
for (i = 0;i < 10;i++)
{
ps->arr[i] = i + 1;
}
//调整空间
ptr = realloc(ps->arr, 20 * sizeof(int));
if (ptr == NULL)
{
perror("realloc");
return 1;
}
ps->arr = ptr;
//继续使用空间
//…………
//释放
free(ps->arr);
free(ps);
ps = NULL;
return 0;--对比上面代码,同样可以完成任务,但是使用柔性数组有两个好处:

--C/C++程序内存分配的几个区域: