引言
灵活内存治理的函数有:malloc,calloc,ralloc,free,本文解说灵活内存函数和经常使用,如何进执行态内存治理,成功通信录咨询人容量的灵活化,对经常出现灵活内存失误启动总结。
目录
引言
为什么存在灵活内存调配
malloc
灵活内存空间位置
内存走漏疑问
free
calloc
realloc
realloc如何开拓灵活内存空间
灵活版本的通信录
经常出现灵活内存失误
1.对NULL空指针启动解援用
2.对灵活开拓空间的越界访问
3.对非灵活开拓的内存经常使用free监禁
4.经常使用free监禁灵活开拓内存的一部分
5.对同一块灵活内存屡次监禁
6.灵活开拓内存遗记监禁(内存监禁)
灵活通信录的成功
为什么存在灵活内存调配
int main()
{int arr[20] = {0};//整形数组开拓20个元素,就是80个字节。return 0;
}
1.空间开拓大小是固定的
2.数组在声明的时刻,必定指定数组的长度,它所须要的内存在编译时调配。
假设咱们开拓的空间不够,那么启动修正会比拟费事。假设开拓的空间较大,那么所占据的空间又会很大。那么有没有一种方法可以用多少空间就开拓多大的内存呢?
这个时刻就有了灵活内存开拓。
malloc
灵活内存函数的头文件 <stdlib.h>
void* malloc (size_t size)
放开内存块,
size
就是咱们要放开的字节大小。当然,咱们的内存是有限的,不是想要放开多少内存就可以放开多少内存。
前往值
- 假设开拓成功,则前往一个指向开拓好空间的指针。
- 假设开拓失败,则前往一个NULL指针,因此malloc的前往值必定要做审核。
- 前往值的类型是void*,所以malloc函数并不知道开拓空间的类型,详细在经常使用的时刻经常使用者自己来选择。
开创的空间放回地址是void*类型,经常使用的时刻记得要强迫类型转换。
所以咱们在经常使用malloc函数时,要检测前往值p能否是空指针。
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include<stdio.h>
int main()
{int arr[10] = { 0 };//灵活内存开拓int* p = (int*)malloc(40);if (p == NULL) //检测前往的p是不是空指针。{printf("%s\n", strerror(errno));return 1;}//经常使用灵活内存int i = 0;for (i = 0; i < 10; i++){*(p + i) = i;}for (i = 0; i < 10; i++){printf("%d ", *(p + i));}//监禁free(p); //经常搭配malloc、calloc、realloc经常使用,前面会讲到p = NULL;return 0;
}
灵活内存空间位置
灵活内存函数是在堆区开拓内存空间的。咱们普通的部分变量、方式参数都是寄存在栈区。
内存走漏疑问
什么是内存走漏?
咱们创立的部分变量,数据会在函数完结时监禁。
灵活内存空间寄存暂时经常使用的数据,这些数据不用等到函数完结时监禁,而是须要时随时开拓,不须要时,随时监禁。灵活开拓的内存经常使用完是要启动监禁的,假设不对内存启动监禁,那么开拓的灵活内存就会被之前的数据占据,这部分的内存就无法经常使用,相当于失落了内存。因此咱们把这类疑问叫做 内存走漏 。
free
void free (void* ptr)
free 函数是专门用来监禁灵活开拓的内存。
- 假设参数ptr指向的空间不是灵活开拓的,那 free 函数的行为是未定义的。
- 假设参数ptr 是NULL指针,则函数什么事都不做。
free 函数只能监禁灵活内存开拓的空间,假设监禁其余空间,就会报错。
#include <stdlib.h>
int main()
{int a = 0;int* p = &a;free(p);p = NULL;return 0;
}
calloc
void* calloc (size_t num, size_t size)
num
开拓空间元素的个数
size
空间中每个元素的大小。
上方的例子就是解释:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{int* p = (int*)calloc(10, sizeof(int));if (p == NULL){printf("%s\n", strerror(errno));}//打印int i = 0;for (i = 0; i < 10; i++){printf("%d ", *(p + i));}//监禁free(p);p = NULL;return 0;
}
realloc
void* realloc (void* ptr, size_t size)
ptr
要调整的空间的起始位置。
size
- realloc函数的发生让灵活内存治理愈加灵敏。
- 有时咱们发现过去放开的空间太小了,有时刻咱们又会感觉放开的空间豁达了,那为了正当的内存,咱们必定对内存的大小做灵敏的调整。那realloc函数可以做到对灵活开拓内存大小的调整。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{int* p = (int*)malloc(40);if (p == NULL){printf("%s\n", strerror(errno));}//经常使用int i = 0;for (i = 0; i < 10; i++){*(p + i) = i + 1;}//扩容int* ptr = realloc(p, 80);if (ptr != NULL) //这里不间接将新的地址赋给p,是由于realloc有或许开拓失败前往空指针。前面详细讲{p = ptr;}for(i=0;i<10;i++){printf("%d ", *(p + i));}//监禁free(p);p = NULL;return 0;
}
realloc如何开拓灵活内存空间
realloc在调整内存空间的是存在两种状况。
第一种状况,原空间之后有足够大的空间。 当要启动调整的内存空间前面有多余的40个字节空间,那么就可以间接开拓向前面开拓40个字节的空间,而后放回起始位置的地址,这里指的是0的地址。
第二种状况 , 原空间之后没有足够大的空间。 要调整的内存空间前面无余以寄存40个字节的空间,那么就要从新找到一个新的地址(可以寄存80个字节)开拓,开拓成功后前往起始位置的地址。
留意:假设须要开拓的空间过大,是会开拓失败的,开拓失败,realloc前往空指针,所以要审核空指针。
这里把realloc开拓的空间换成8000,来成功第二种状况。
灵活版本的通信录
灵活:
要成功通信录容量的灵活化,要成功两个配置
1.通信录自动能寄存3团体的消息
2.假设空间不够了,就参与空间,每次参与2团体的空间
静态:
原来的通信录的消息由结构体组成的 数组 ,来寄存消息,设置的是100人的消息。
1.当没有100人的消息时,会形成空间上的糜费。
2.当超越100人的消息时,又无法智能扩容,假构想要扩容要手动扭转最大寄存的空间。
原通信录的代码:
有兴味可以学习通信录的成功http://t.csdnimg.cn/UbT9I
寄存数据的扭转
首先把寄存咨询人消息的结构体给大家看看
typedef struct PeoInfo
{char name[MAX_NAME];int age;char sex[MAX_SEX];char tele[MAX_TELE];char addr[MAX_ADDR];
}PeoInfo;
原来寄存咨询人的消息是经过数组data[100],这样的数组的空间是固定的,是一开局设置的最大容量。
typedef struct Contact
{PeoInfo>typedef struct Contact
{PeoInfo*>void InitContact(Contact* pc)
{assert(pc);pc->count = 0;memset(pc->data, 0, sizeof(pc->data));
}
灵活通信录要成功开拓3个咨询人的空间,并将它们启动初始化。 既要开拓空间,又要启动初始化,咱们想到 calloc 函数。
malloc 是单纯地开拓空间, realloc 是既开拓空间,并启动初始化。 malloc 和 realloc 地域别就在于能否对开拓的空间初始化。
开拓3个咨询人空间, calloc 启动开拓,将地址传给pc->data.将记载咨询人的容量传给capacity.
int InitContact(Contact* pc)
{assert(pc);pc->count = 0;pc->data =(int*) calloc(3, sizeof(PeoInfo));if (pc == NULL){printf("InitContact::%s\n", strerror(errno));return 1;}pc->capacity = 3;return 0;
}
2.参与咨询人
参数pc,是创立的 struct Contact 结构体变量的地址,这里是 传址调用, 作用就是扭转原来的数据。
灵活内存治理, 经过pc->data[count]可以启动数据的输入。最关键的是成功通信录容量的灵活化。
count示意曾经经常使用的咨询人数量,capacity示意咨询人的总容量。
当count == capacity时,就要灵活开拓内存,对容量启动增容。
CheckCapacity为自定义增容函数,咱们要成功增容的配置。
realloc 从新开拓内存块,成功内存的灵活化。 realloc 前往的起始地址不能间接传给 data, 由于灵活内存的开拓有或许失败,失败传回空指针。
capacity 参与2,最后揭示增容成功。
void CheckCapacity(Contact* pc)
{if (pc->count == pc->capacity);{PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + 2) * sizeof(PeoInfo));if (ptr == NULL){printf("AddContact::%s\n", strerror(errno));return 1;}else{pc->data = ptr;pc->capacity += 2;printf("增容成功\n");}}
}
void AddContact(Contact* pc)
{assert(pc);//增容CheckCapacity(pc);printf("请输入名字:》");scanf("%s", pc->data[pc->count].name);printf("请输入年龄:》");scanf("%d", &(pc->data[pc->count].age));printf("请输入性别:>");scanf("%s", pc->data[pc->count].sex);printf("请输入电话:>");scanf("%s", pc->data[pc->count].tele);printf("请输上天址:>");scanf("%s", pc->data[pc->count].addr);pc->count++;printf("参与成功\n");
}
将全体的代码呈如今文章末尾。
经常出现灵活内存失误
1.对NULL空指针启动解援用
假设开拓的空间过大,malloc有或许开拓失败,开拓失败就会前往空指针。假设间接对p启动解援用,就会发生疑问。
#include <stdlib.h>
int main()
{int* p = (int*)malloc(40);*p = 20;return 0;
}
正确处置方法:
在开拓灵活内存后对p启动测验,能否为空指针。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{int* p = (int*)malloc(40);if (p == NULL){printf("%s\n", strerror(errno));return 1;}*p = 20;free(p);p = NULL;return 0;
}
2.对灵活开拓空间的越界访问
咱们只开拓了10个字节的空间,然而访问,从0到10,10算出来的话就是11个元素,这里访问越界了,就会出疑问。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{int* p = (int*)malloc(40);if (p == NULL){printf("%s\n", strerror(errno));return 1;}//经常使用int i = 0;for (i = 0; i <= 10;i++){p[i] = i;}free(p);p = NULL;return 0;
}
正确处置方法:
必定要留意咱们开拓的空间能否和访问的空间是一样的。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{int* p = (int*)malloc(40);if (p == NULL){printf("%s\n", strerror(errno));return 1;}//经常使用int i = 0;for (i = 0; i < 10;i++){p[i] = i;}free(p);p = NULL;return 0;
}
3.对非灵活开拓的内存经常使用free监禁
free只能够监禁灵活开拓的内存,不能够轻易去经常使用函数。
#include <stdlib.h>
int main()
{int a = 10;int* p = &a;//....free(p);p = NULL;return 0;
}
4.经常使用free监禁灵活开拓内存的一部分
free做不到监禁灵活内存的一部分,假设要监禁要将整个灵活内存启动监禁。
#include <stdlib.h>
int main()
{int* p = (int*)malloc(40);if (p = NULL){return 1;}int i = 0;for (i = 0; i < 10; i++){*p = i;p++;}free(p);p = NULL;return 0;
}
正确处置方法:
不扭转p的位置,对p启动监禁。经常使用p的话,经过:
*(p+i) = i;
5.对同一块灵活内存屡次监禁
free的二次经常使用,第二次经常使用的灵活内存空间曾经还给操作系统了,然而还能对p启动操作,就是野指针疑问。
#include <stdlib.h>
int main()
{int* p = (int*)malloc(40);free(p);//....free(p);return 0;
}
正确处置方法:
防止free的二次经常使用,或许将p转化为空指针。
#include <stdlib.h>
int main()
{int* p = (int*)malloc(40);free(p);p = NULL;free(p);return 0;
}
6.灵活开拓内存遗记监禁(内存监禁)
看看上方的例子:
假设flag = 5的话,那么前面free函数就会跳过,灵活开拓的内存就不能监禁。
#include <stdio.h>
#include <stdlib.h>
void test()
{int* p = (int*)malloc(100);int flag = 0;scanf("%d", &flag);if (flag == 5){return;}free(p);p = NULL;
}
int main()
{test();return 0;
}
灵活通信录的成功
contact.h
头文件:用来对函数的声明
#pragma once#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>#define DEFAULT_SZ 3
#define INC_SZ 2
#define MAX 100
#define MAX_NAME 20
#define MAX_SEX 10
#define MAX_TELE 12
#define MAX_ADDR 30//类型的声明
typedef struct PeoInfo
{char name[MAX_NAME];int age;char sex[MAX_SEX];char tele[MAX_TELE];char addr[MAX_ADDR];
}PeoInfo;typedef struct Contact
{PeoInfo*>#define _CRT_SECURE_NO_WARNINGS#include "contact.h"//灵活版本
int InitContact(Contact* pc)
{assert(pc);pc->count = 0;pc->data =(int*) calloc(DEFAULT_SZ, sizeof(PeoInfo));if (pc == NULL){printf("InitContact::%s\n", strerror(errno));return 1;}pc->capacity = DEFAULT_SZ;return 0;
}void DestroyContact(Contact* pc)
{assert(pc);free(pc->data);pc->data = NULL;
}void CheckCapacity(Contact* pc)
{if (pc->count == pc->capacity);{PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo));if (ptr == NULL){printf("AddContact::%s\n", strerror(errno));return 1;}else{pc->data = ptr;pc->capacity += INC_SZ;printf("增容成功\n");}}
}
void AddContact(Contact* pc)
{assert(pc);//增容CheckCapacity(pc);printf("请输入名字:》");scanf("%s", pc->data[pc->count].name);printf("请输入年龄:》");scanf("%d", &(pc->data[pc->count].age));printf("请输入性别:>");scanf("%s", pc->data[pc->count].sex);printf("请输入电话:>");scanf("%s", pc->data[pc->count].tele);printf("请输上天址:>");scanf("%s", pc->data[pc->count].addr);pc->count++;printf("参与成功\n");
}void ShowContact(const Contact* pc)
{assert(pc);int i = 0;//一个汉字是两个字符printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "名字", "年龄", "性别", "电话", "地址");for (i = 0; i < pc->count; i++){printf("%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n", pc->data[i].name,pc->data[i].age,pc->data[i].sex,pc->data[i].tele,pc->data[i].addr);}
}static int FindByName(Contact* pc, char name[])
{assert(pc);int i = 0;for (i = 0; i < pc->count; i++){if (0 == strcmp(pc->data[i].name, name)){return i;}}return -1;
}void DelContact(Contact* pc)
{char name[MAX_NAME] = { 0 };assert(pc);int i = 0;if (pc->count == 0){printf("通信录为空,没有消息可以删除\n");return;}printf("请输入要删除人的名字:>");scanf("%s", name);//删除//1.查找int pos = FindByName(pc, name);if (pos == -1){printf("要删除的人不存在\n");return;}//2.删除for (i = pos; i < pc->count; i++){pc->data[i] = pc->data[i + 1];}pc->count--;
}void SeachContact(Contact* pc)
{assert(pc);char name[MAX_NAME] = { 0 };printf("请输入须要查找的咨询人的名字:>");scanf("%s", name);//1.查找int pos = FindByName(pc, name);if (pos == -1){printf("要查找的人不存在\n");return;}//2.打印printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "名字", "年龄", "性别", "电话", "地址");printf("%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n", pc->data[pos].name,pc->data[pos].age,pc->data[pos].sex,pc->data[pos].tele,pc->data[pos].addr);}void ModifyContact(Contact* pc)
{assert(pc);char name[MAX_NAME] = { 0 };printf("请输入须要查找的咨询人的名字:>");scanf("%s", name);//1.查找int pos = FindByName(pc, name);if (pos == -1){printf("要查找的人不存在\n");return;}printf("要修正人的消息曾经找到,接上去启动修正\n");//2.修正printf("请输入名字:》");scanf("%s", pc->data[pos].name);printf("请输入年龄:》");scanf("%d", &(pc->data[pos].age));printf("请输入性别:>");scanf("%s", pc->data[pos].sex);printf("请输入电话:>");scanf("%s", pc->data[pos].tele);printf("请输上天址:>");scanf("%s", pc->data[pos].addr);printf("修正成功\n");
}int cmp_peo_by_name(const void* e1, const void* e2)
{return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}
//依照名字来排序
void SortContact(Contact* pc)
{assert(pc);qsort(pc->data, pc->count, sizeof(PeoInfo), cmp_peo_by_name);printf("排序成功\n");
}
test.c
通信录主头绪:
#define _CRT_SECURE_NO_WARNINGS#include "contact.h"void menu()
{printf("**********************************************\n");printf("******** 1.add 2.del ********\n");printf("******** 3.search 4.modify ********\n");printf("******** 5.show 6.sort ********\n");printf("******** 0.exit ********\n");printf("**********************************************\n");}
int main()
{int input = 0;Contact con;//初始化通信录:模块化初始化InitContact(&con);//只能传地址,启动修正do{menu();printf("请选用:》");scanf("%d", &input);switch (input){case 1:AddContact(&con);break;case 2:DelContact(&con);break;case 3:SeachContact(&con);break;case 4:ModifyContact(&con);break;case 5:ShowContact(&con);break;case 6:SortContact(&con);break;case 0:DestroyContact(&con);printf("分放开信录\n");break;default:printf("选用失误\n");}} while (input);return 0;
}