为什么会存在动态内存管理呢?
我们经常使用的是:
#include<stdio.h> int main(){ int i = 0; char arr[10] = { 0 }; return 0; }
1.这两种情况是在栈上开辟的 2.数组在声明之前必须指定长度,他所需的的内存在编译的时候分配 2。有时候我们在使用数组之前不知道,要用多大的空间,以上这种方法就不能满足了
动态内存函数的介绍
malloc和free介绍
这个函数是向内存申请一块连续的内存空间并且返回这块空间起始的指针 1.如果开辟成功了,返回这块空间的起始指针 2.如果开辟失败,返回NULL指针 3. 返回值是void ,参数是size_t ,意思是你在形参哪里输入你要开辟多少字节的空间*
1.free函数用来释放动态开辟的内存。 如果中放的指针指向的空间不是动态开辟的,那free函数的行为是未定义的。如果是NULL指针,则函数什么事都不做 malloc和free都声明头文件<stdlib.h> 看如何引用的
int main(){
int num = 0;
scanf("%d", &num);
int* p = malloc(num*sizeof(int));
int i = 0;
if (p != NULL){
for (i = 0; i < num; i++){
*(p + i) = 8;
}
}
for (i = 0; i < num; i++){
printf("%d ", p[i]);
}
free(p);
p = NULL;
return 0;
}
malloc开辟之后没有赋值的情况
calloc介绍
1 第一个参数:你要开辟的空间个数,第二个参数:是开辟空间的大小 2 与malloc的区别是这个开辟空间每个字节都置为0 引用:
int main(){
int *p = calloc(10, sizeof(int));
if (p != NULL){
int i = 0;
for (i = 0; i < 10; i++){
printf("%d ",*(p + i));
}
}
free(p);
p = NULL;
return 0;
}
realloc介绍
realloc大白话说就是可以改变你开辟的内存空间的大小,有了这个函数是的,开辟内存,和内存使用就很灵活了 第一个参数是你要改变空间的地址,第二个是要改成多大的,返回的地址是你改完后的空间的首地址,这个分成两种情况
看一下情况
int main(){
int*p = calloc(10, sizeof(char));
p = realloc(p, 20);
return 0;
}
解决:
int main(){
int*p = calloc(10, sizeof(int));
int*ptr = realloc(p, 80);
if (ptr != NULL){
p=ptr;
}
int i= 0;
for (i = 0; i < 20; i++){
printf("%d ", p[i]);
}
free(p);
p = NULL;
return 0;
}
常见的几种动态内存错误
1,对NULL指针的解引用操作。
int main(){
int *p = (int*)malloc(1000);
int i = 0;
for (i = 0; i < 10; i++){
*(p + i) = i;
}
return 0;
}
2,动态空间的越界访问
int main(){
int *p = (int*)malloc(10 * sizeof(int));
if (p != NULL){
int i = 0;
for (i = 0; i < 40; i++){
*(p + i) = i;
}
}
free(p);
p = NULL;
return 0;
}
3,对非动态内存free释放
4,使用free释放动态内存的一部分
int main(){
int *p = (int*)malloc(10 * sizeof(int));
if (p != NULL){
int i = 0;
for (i = 0; i < 5; i++){
*p++ = i;
}
}
free(p);
p = NULL;
return 0;
}
5,动态内存倍多次释放
6,动态内存忘记释放
1 动态内存可以用free释放 2 程序结束
几道经典的笔试题
第一道:
#include<stdio.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; }
第二道有女朋友的都会做:
char *GetMemory(void) { char p[] = “hello world”; return p; } void Test(void) { char *str = NULL; str = GetMemory(); printf(str); } int main(){ Test(); return 0; }
第三道:
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 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; }
总结: 1.局部变量和函数形参实在栈上开辟的,出了作用域就会自动还给操作系统 2.而动态内存开辟,在堆上开辟的,需要手动销毁,或者程序结束自动销毁
柔性数组
柔性数组不咋常见,结构中的最后一个元素允许是未知大小的数组,这就叫做『柔性数组』成员。 **特点: 结构中的柔性数组成员前面必须至少一个其他成员。 sizeof 返回的这种结构大小不包括柔性数组的内存。
包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小
int arr[];就是柔性数组成员,也可以是int arr[0]; 这样写 我们看看是如何使用的:
typedef struct str{
int a;
int arr[];
}pf;
int main(){
pf *p = (pf*)malloc(sizeof(pf)+10 * sizeof(int));
p->a = 10;
int i = 0;
for (i = 0; i < p->a; i++)
{
p->arr[i] = i;
printf("%d ", p->arr[i]);
}
return 0;
}
看到这是不是感觉,有没有柔性数组都行呀,不就是为int a 和 int arr[]动态开辟空间吗,分开辟不久完了吗,其实不然,看代码
typedef struct str{
int a;
int *arr;
}pf;
int main(){
pf *ps = (pf*)malloc(sizeof(pf));
if (ps == NULL){
return 1;
}
ps->arr = (pf*)malloc(10 * sizeof(int));
int i = 0;
for (i = 0; i < 10; i++){
ps->arr[i] = i;
}
free(ps->arr);
ps->arr = NULL;
free(ps);
ps = NULL;
return 0;
}
这是相当于开辟了两次呀;由于你是开辟了两次,你就得释放两次呀 为啥分先后呢,你先把ps释放了,就找不到arr指针指向的那块空间了 柔性数组的好处:方便内存释放,释放一次就OK
|