1. 前言
之前向大家介紹了C語言實(shí)現(xiàn)通訊錄管理系統(tǒng)1.0版本,但該版本有明顯的不足之處,比如:一開始就開辟了1000個(gè)date數(shù)組,如果聯(lián)系人很少,那么就會(huì)造成嚴(yán)重的內(nèi)存浪費(fèi),或者聯(lián)系人超過了1000人,那么原數(shù)組就放不下了,所以今天我們考慮使用動(dòng)態(tài)內(nèi)存管理的辦法來實(shí)現(xiàn)一個(gè)內(nèi)存會(huì)隨著聯(lián)系人的增加而增加的通訊錄動(dòng)態(tài)增長版!
首先我們先來學(xué)習(xí)如何進(jìn)行動(dòng)態(tài)內(nèi)存分配及動(dòng)態(tài)內(nèi)存函數(shù)的使用方法。
2. 動(dòng)態(tài)內(nèi)存函數(shù)
2.1 malloc函數(shù)
定義:void* malloc(size_t size);
功能:向內(nèi)存申請(qǐng)一塊連續(xù)可用的空間,并返回指向這塊空間的指針。
注意事項(xiàng):
size的單位是字節(jié)
如果開辟成功,則返回一個(gè)指向開辟好空間的指針。
如果開辟失敗,則返回一個(gè)NULL指針,因此malloc的返回值一定要做檢查。
返回值的類型是void* ,所以malloc函數(shù)并不知道開辟空間的類型,具體在使用的時(shí)候使用者自己來決定,使用前強(qiáng)制類型轉(zhuǎn)換。
如果參數(shù)size為0,malloc的行為是標(biāo)準(zhǔn)是未定義的,取決于編譯器。
使用方法:
int main()
{
//申請(qǐng)空間
int* ptr = (int*)malloc(40);
int* p = ptr;
if (p == NULL)
{
perror("malloc");
return 1;
}
int i = 0;
for (i = 0; i < 10; i++)
{
*p = i;
p++;
}
//釋放空間
free(ptr);
ptr = NULL;
return 0;
}
2.2 free函數(shù)
定義:void free(void* str);
功能:free函數(shù)用來釋放動(dòng)態(tài)開辟的內(nèi)存。
注意事項(xiàng):
如果參數(shù)ptr指向的空間不是動(dòng)態(tài)開辟的,那free函數(shù)的行為是未定義的。
如果參數(shù)ptr是NULL指針,則函數(shù)什么事都不做。
當(dāng)我們不釋放動(dòng)態(tài)申請(qǐng)的內(nèi)存的時(shí)候:
如果程序結(jié)束,動(dòng)態(tài)申請(qǐng)的內(nèi)存由操作系統(tǒng)自動(dòng)回收。
但是如果程序不結(jié)束,動(dòng)態(tài)內(nèi)存是不會(huì)自動(dòng)回收的,就會(huì)形成內(nèi)存泄露的問題。
所以只要?jiǎng)討B(tài)開辟了內(nèi)存,使用完就需要用free函數(shù)釋放。
2.3 calloc函數(shù)
定義:void* calloc(size_t num, size_t size);
功能:為num個(gè)大小為size的元素開辟一塊空間,并且把空間的每個(gè)字節(jié)初始化為0。
注意事項(xiàng):
與函數(shù)malloc的區(qū)別只在于calloc會(huì)在返回地址之前把申請(qǐng)的空間的每個(gè)字節(jié)初始化為全0。
使用方法:
int main()
{
//申請(qǐng)空間
int* ptr = (int*)calloc(10, sizeof(int));
int* p = ptr;
if (p == NULL)
{
perror("calloc");
}
//使用空間
//釋放空間
free(ptr);
ptr = NULL;
return 0;
}
2.4 realloc函數(shù)
定義:void* realloc(void* ptr, size_t size);
功能:realloc函數(shù)可以做到對(duì)動(dòng)態(tài)開辟內(nèi)存大小的調(diào)整。
注意事項(xiàng):
ptr是要調(diào)整的內(nèi)存地址。
size調(diào)整之后新大小。
返回值為調(diào)整之后的內(nèi)存起始位置。
這個(gè)函數(shù)在調(diào)整原內(nèi)存空間大小的基礎(chǔ)上,還會(huì)將原來內(nèi)存中的數(shù)據(jù)移動(dòng)到新的空間。
realloc在調(diào)整內(nèi)存空間的是存在兩種情況:
情況1:原有空間之后有足夠大的空間
擴(kuò)展內(nèi)存就直接原有內(nèi)存之后直接追加空間,原來空間的數(shù)據(jù)不發(fā)生變化。
情況2:原有空間之后沒有足夠大的空間
擴(kuò)展的方法是:在堆空間上另找一個(gè)合適大小的連續(xù)空間來使用。這樣函數(shù)返回的是一個(gè)新的內(nèi)存地址。
此時(shí)就要注意,如果整個(gè)堆區(qū)都沒有一個(gè)合適大小的連續(xù)空間,就會(huì)申請(qǐng)失敗。
所以使用完realloc函數(shù)應(yīng)該先判斷返回的地址是否為空,不為空就可以正常使用了。
使用方法:
int main()
{
//申請(qǐng)空間
int* ptr = (int*)malloc(40);
if (ptr == NULL)
{
perror("malloc");
return 1;
}
//使用空間
int* p = ptr;
int i = 0;
for (i = 0; i < 10; i++)
{
*p = i;
p++;
}
//擴(kuò)容空間
int* p2 = (int*)realloc(ptr, 80);
if (p2 == NULL)
{
perror("realloc");
}
p = p2;
//使用空間
//釋放空間
free(p);
p = NULL;
return 0;
}
3. 優(yōu)化通訊錄程序
學(xué)習(xí)完這些動(dòng)態(tài)內(nèi)存函數(shù)以后,我們就可以用動(dòng)態(tài)內(nèi)存分配來優(yōu)化通訊錄代碼,避免內(nèi)存浪費(fèi)和內(nèi)存不足的問題。
3.1 通訊錄的優(yōu)化
我們原來創(chuàng)建的通訊錄是直接創(chuàng)建了1000個(gè)元素,而動(dòng)態(tài)內(nèi)存版本我們首先應(yīng)該考慮如何修改通訊錄,來滿足我們的需求,這里我有兩個(gè)想法:
1.通訊錄初始只能存放3個(gè)聯(lián)系人的信息。
2.當(dāng)通訊錄滿的時(shí)候,每次增加2個(gè)人的空間。
這樣修改的話,既避免了內(nèi)存的浪費(fèi),也不會(huì)出現(xiàn)內(nèi)存不夠的情況。
具體修改方式如下:
//靜態(tài)版本
//typedef struct contact
//{
// people date[MAX];//1000人的信息
// int count;//已保存的信息個(gè)數(shù)
//}contact;
//動(dòng)態(tài)版本
typedef struct contact
{
people* date;//1000人的信息
int count;//已保存的信息個(gè)數(shù)
int cap;//記錄當(dāng)前通訊錄的最大容量
}contact;
3.2 初始化通訊錄的優(yōu)化
接下來就該初始化了,初始化通訊錄的時(shí)候用malloc函數(shù)來開辟內(nèi)存。
//靜態(tài)版本
//void InitContact(contact* pc)
//{
// assert(pc);
// pc->count = 0;
// memset(pc->date, 0, sizeof(pc->date));
//}
//動(dòng)態(tài)版本
void InitContact(contact* pc)
{
assert(pc);
pc->count = 0;
pc->cap = DEFAULT_SIZE;
pc->date = (people*)malloc((pc->cap) * (sizeof(people)));
if (pc->date == NULL)
{
perror("InitContact::malloc");
return;
}
memset(pc->date, 0, (pc->cap) * (sizeof(people)));
}
當(dāng)退出通訊錄的時(shí)候,我們要釋放動(dòng)態(tài)開辟的內(nèi)存,所以還應(yīng)該添加一個(gè)銷毀通訊錄的函數(shù)。
void DestroyContact(contact* pc)
{
free(pc->date);
pc->date = NULL;
pc->cap = 0;
pc->count = 0;
printf("銷毀成功\n");
}
3.3 添加聯(lián)系人的優(yōu)化
接下來就是添加聯(lián)系人的時(shí)候加入判斷函數(shù),判斷此時(shí)通訊錄是否為滿,如果滿了就再開辟2個(gè)聯(lián)系人的內(nèi)存,每次添加都應(yīng)該判斷一次。
void CheckCapacity(contact* pc)
{
if (pc->count == pc->cap)
{
people* ptest = (people*)realloc(pc->date, ((pc->cap) + 2) * (sizeof(people)));
if (ptest != NULL)
{
pc->date = ptest;
}
else
{
perror("CheckCapacity::realloc");
return;
}
pc->cap += 2;
printf("擴(kuò)容成功\n");
}
}
void AddPeople(contact* pc)
{
assert(pc);
//靜態(tài)版本
//if (pc->count == MAX)
//{
// printf("通訊錄已滿,無法添加\n");
// return;
//}
//動(dòng)態(tài)版本
CheckCapacity(pc);
printf("請(qǐng)輸入姓名:\n");
scanf("%s", pc->date[pc->count].name);
printf("請(qǐng)輸入性別:\n");
scanf("%s", pc->date[pc->count].sex);
printf("請(qǐng)輸入年齡:\n");
scanf("%d", &pc->date[pc->count].age);
printf("請(qǐng)輸入號(hào)碼:\n");
scanf("%s", pc->date[pc->count].tele);
printf("請(qǐng)輸入地址:\n");
scanf("%s", pc->date[pc->count].address);
pc->count++;
printf("添加成功\n");
}
3.4 運(yùn)行測試
我們來運(yùn)行代碼,觀察結(jié)果
可以看出,這時(shí)候代碼已經(jīng)優(yōu)化完成,初始只能保存3個(gè)聯(lián)系人的信息,當(dāng)繼續(xù)存儲(chǔ)時(shí)就會(huì)擴(kuò)容。如果退出程序,就會(huì)銷毀(釋放)掉動(dòng)態(tài)內(nèi)存分配的空間,不會(huì)造成內(nèi)存泄漏的問題。文章來源:http://www.zghlxwxcb.cn/news/detail-403047.html
4. 結(jié)尾
到這里,我們動(dòng)態(tài)增長版本的通訊錄管理系統(tǒng)2.0也就優(yōu)化完成了,后續(xù)我還會(huì)對(duì)代碼進(jìn)行優(yōu)化,敬請(qǐng)期待。文章來源地址http://www.zghlxwxcb.cn/news/detail-403047.html
到了這里,關(guān)于動(dòng)態(tài)內(nèi)存管理函數(shù)介紹及C語言實(shí)現(xiàn)通訊錄管理系統(tǒng)2.0版(動(dòng)態(tài)增長版本)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!