【day25】动态内存

#define _CRT_SECURE_NO_WARNINGS 1

#include

  • 动态内存分配
    • 动态内存分配的意义
    • 动态内存函数的介绍
      • malloc和free
      • 动态内存的释放和回收
      • calloc
      • realloc
  • 常见的动态内存错误的例子

动态内存分配

动态内存分配的意义

int val = 20;//在栈空间上开辟四个字节
char arr[10] = {0};//在栈空间上开辟10个字节的连续空间

上述的开辟空间的方式有两个特点:

  1. 空间开辟大小是固定的。
  2. 数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配。
    但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道,
    那数组的编译时开辟空间的方式就不能满足了。这时候就只能试试动态存开辟了
||———————————————————-||
||栈区|存储局部变量    ||
||    |函数的参数      ||
||————————————————————||
||堆区|动态内存分配的  ||
||    |malloc  calloc ||
||    |free   realloc ||
||————————————————————||
||静态区|全局变量      ||
||      |static修饰   ||
||      |的静态变量    ||
||—————————————————————||

动态内存函数的介绍

malloc和free

动态内存开辟的函数

void* malloc (size_t size);

这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。
如果开辟成功,则返回一个指向开辟好空间的指针。
如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。
如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器。

动态内存的释放和回收

void free (void* ptr);

free函数用来释放动态开辟的内存。
如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
如果参数 ptr 是NULL指针,则函数什么事都不做。

malloc和free都声明在 stdlib.h 头文件中。

int main() {//假设开辟10个整型的空间int arr[10];//栈区int* p = (int*)malloc(100* sizeof(int));//使用这些空间时if (p == NULL) {perror("main");return 0;}//使用int i = 0;for (i = 0; i < 10; i++) {*(p + i) = i;}for (i = 0; i < 10; i++) {printf("%d ", p[i]);}//回收空间free(p);//此时指针p还指向已经还回去的内存呢地址,需要手动置为空指针p = NULL;return 0;
}

calloc

void* calloc (size_t num, size_t size);

函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0。有效结果是分配零初始化的字节内存块。
如果为零,则返回值取决于特定的库实现(它可能是也可能不是空指针),但返回的指针不应被取消引用。

与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0。

int main()
{int* p = (int*)calloc(10, sizeof(int));if (NULL != p){//使用空间int i = 0;for (i = 0; i < 10; i++) {printf("%d ", p[i]);}}free(p);p = NULL;return 0;
}

realloc

realloc函数(重新开辟空间)的出现让动态内存管理更加灵活。
合理的分配内存,我们一定会对内存的大小做灵活的调整。那 realloc 函数就可以做到对动态开辟内存大小的调整

void* realloc (void* ptr, size_t size);

ptr 是要调整的内存地址 size 调整之后新大小 返回值为调整之后的内存起始位置。
更改所指向的内存块的大小。该函数可以将内存块移动到新位置(其地址由函数返回)。
内存块的内容将保留到新旧大小中较小的一个,即使该块被移动到新位置也是如此。如果新部分较大,则新分配部分的值不确定。
如果这是一个空指针,则该函数的行为类似于 ,分配一个新的字节块并返回指向其开头的指针。
如果函数无法分配请求的内存块,则返回空指针,并且参数所指向的内存块未解除分配(它仍然有效,并且其内容不变)

realloc在调整内存空间的是存在两种情况:
情况1:原有空间之后有足够大的空间; 情况2:原有空间之后没有足够大的空间
当是情况1 的时候,要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化。
当是情况2 的时候,原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小的连续空间来使用。这样函数返回的是一个新的内存地址。

int main()
{int* p = (int*)calloc(10, sizeof(int));if (NULL != p){//使用空间int* ptr = (int*)realloc(p, 20 * sizeof(int));if (ptr != NULL) {p = ptr;}int i = 0;for (i = 0; i < 20; i++) {*(p + i) = i; printf("%d ", p[i]);}//这里需要p指向的空间更大,需要20个空间。}free(p);p = NULL;return 0;
}

常见的动态内存错误的例子

1.对空指针的解引用操作

 void test()
{int *p = (int *)malloc(INT_MAX/4);//需要判断是否是空指针*p = 20;//如果p的值是NULL,就会有问题free(p);
}

2.对动态开辟空间的越界访问

void test()
{int i = 0;int *p = (int *)malloc(10*sizeof(int));if(NULL == p){exit(EXIT_FAILURE);}for(i=0; i<=10; i++){*(p+i) = i;//当i是10的时候越界访问printf("%d ", p[i]);}free(p);
}

3.对非动态开辟内存使用free释放

void test()
{int a = 10;int *p = &a;free(p);//不能释放非动态内存
}

4.使用free释放一块动态开辟内存的一部分

void test()
{int *p = (int *)malloc(100);p++;free(p);//p不再指向动态内存的起始位置
}

5.对同一块动态内存多次释放

void test()
{int *p = (int *)malloc(100);free(p);free(p);//重复释放
}

6.动态开辟内存忘记释放(内存泄漏)

void test()
{int *p = (int *)malloc(100);if(NULL != p){*p = 20;}
}int main(){test();while(1);
}

忘记释放不再使用的动态开辟的空间会造成内存泄漏。
切记: 动态开辟的空间一定要释放,并且正确释放 。

int main() {test();return 0;
}

对昨天写的通讯录功能,使用动态内存进行改造

test.c 文件中的case EXIT下

case EXIT://销毁通讯录DestoryContact(&con);printf("退出通讯录\n");break;

contact.h下更新通讯录结构体的成员,并添加一个函数声明

#define DEFAULT_SZ 3
#define INC_SZ 2
typedef struct Contact
{PeoInfo *data;//指向动态申请的空间,用来存放联系人的信息int sz;//记录当前通讯录中有效的信息个数int capacity;//记录当前通讯录的最大容量
}Contact;void DestoryContact(Contact* pc);//销毁信息

contact.c 文件里新加一个销毁函数并且对原本的初始化函数和添加函数做动态分配内存改造。

void InitContact(Contact* pc)//初始化函数
{pc->data = (PeoInfo*)malloc(DEFAULT_SZ * sizeof(PeoInfo));if(pc->data == NULL){perror("InitContact");return;}pc->sz = 0;pc->capacity = DEFAULT_SZ;//默认大小
}void DestoryContact(Contact* pc)//销毁信息
{free(pc->data);pc->data = NULL;pc->sz = NULL;pc->capacity = NULL;
}void AddContact(Contact* pc)//添加人的信息,并更具存量自动扩存
{if (pc->sz == pc->capacity) //达到最大容量,准备调整空间大小{PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo));if (ptr != NULL) {pc->data = ptr;pc->capacity += INC_SZ;printf("增容成功\n");}else{perror("AddContact");printf("增容失败\n");return;}}//增加信息printf("请输入姓名:");scanf("%s",pc->data[pc->sz].name);printf("请输入年龄:");scanf("%d", &(pc->data[pc->sz].age));printf("请输入性别:");scanf("%s", pc->data[pc->sz].sex);printf("请输入电话号码:");scanf("%s", pc->data[pc->sz].tele);printf("请输入地址:");scanf("%s", pc->data[pc->sz].addr);pc->sz++;printf("添加成功\n");
}


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部