免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
動態(tài)內(nèi)存分配
7.4  內(nèi)存的使用
指針是一個非常靈活且強大的編程工具,有非常廣泛的應(yīng)用。大多數(shù)C程序都在某種程度上使用了指針。C語言還進一步增強了指針的功能,為在代碼中使用指針提供了很強的激勵機制,它允許在執(zhí)行程序時動態(tài)分配內(nèi)存。只有使用指針,才能動態(tài)分配內(nèi)存。
第5章的一個程序計算一組學(xué)生的平均分,當(dāng)時它只處理10個學(xué)生。假設(shè)要編寫一個程序,但事先不知道要處理多少個學(xué)生,若使用動態(tài)內(nèi)存分配(dynamic memory allocation),所使用的內(nèi)存就不會比指定的學(xué)生分數(shù)所需的內(nèi)存多??梢栽趫?zhí)行時創(chuàng)建足以容納所需數(shù)據(jù)量的數(shù)組。
在程序的執(zhí)行期間分配內(nèi)存時,內(nèi)存區(qū)域中的這個空間稱為堆(heap)。還有另一個內(nèi)存區(qū)域,稱為堆棧(stack),其中的空間分配給函數(shù)的參數(shù)和本地變量。在執(zhí)行完該函數(shù)后,存儲參數(shù)和本地變量的內(nèi)存空間就會釋放。堆中的內(nèi)存是由程序員控制的。如本章后面所述,在分配堆上的內(nèi)存時,由程序員跟蹤所分配的內(nèi)存何時不再需要,并釋放這些空間,以便于以后重用它們。
7.4.1  動態(tài)內(nèi)存分配:malloc()函數(shù)
在運行時分配內(nèi)存的最簡單的標準庫函數(shù)是malloc()。使用這個函數(shù)時,需要在程序中包含頭文件。使用malloc()函數(shù)需指定要分配的內(nèi)存字節(jié)數(shù)作為參數(shù)。這個函數(shù)返回所分配內(nèi)存的第一個字節(jié)的地址。因為返回的是一個地址,所以這里可以使用指針。
動態(tài)內(nèi)存分配的一個例子如下:
int *pNumber = (int *)malloc(100);
這條語句請求100個字節(jié)的內(nèi)存,并把這個內(nèi)存塊的地址賦予pNumber。只要不修改它,任何時間使用這個變量pNumber,它都會指向所分配的100個字節(jié)的第一個int的位置。這個內(nèi)存塊能保存25個int值,每個int占4個字節(jié)。
注意,類型轉(zhuǎn)換(int*)將函數(shù)返回的地址轉(zhuǎn)換成int類型的指針。這么做是因為malloc()是一般用途的函數(shù),可為任何類型的數(shù)據(jù)分配內(nèi)存。這個函數(shù)不知道要這個內(nèi)存作什么用,所以它返回的是一個void類型的指針,寫做void*。類型void*的指針可以指向任意類型的數(shù)據(jù),然而不能取消對void指針的引用,因為它指向未具體說明的對象。許多編譯器會把malloc()返回的地址自動轉(zhuǎn)換成適當(dāng)?shù)念愋?,且不會傷害具體指定的對象。
可以請求任意數(shù)量的字節(jié),字節(jié)數(shù)僅受制于計算機中未用的內(nèi)存以及malloc()的運用場合。如果因某種原因而不能分配請求的內(nèi)存,malloc()會返回一個NULL指針。這個指針等于0。最好先用if語句檢查請求動態(tài)分配的內(nèi)存是否已分配,再使用它。就如同金錢,沒錢又想花費,會帶來災(zāi)難性的后果。因此,應(yīng)編寫如下語句:
if(pNumber == NULL)
{
/*Code  to  deal  with  no  memory  allocated  */
}
如果指針是NULL,最好執(zhí)行適當(dāng)?shù)牟僮?。例如,至少可以顯示一條信息"內(nèi)存不足",然后中止程序。這比允許程序繼續(xù)執(zhí)行,使之使用NULL地址存儲數(shù)據(jù)導(dǎo)致崩潰要好得多。然而,在某些情況下,可以釋放在別的地方使用的內(nèi)存,以便程序有足夠的內(nèi)存繼續(xù)執(zhí)行下去。
7.4.2  分配內(nèi)存時使用sizeof運算符
前一個例子很不錯,但我們不常處理字節(jié),而常常處理int、double等數(shù)據(jù)類型。例如給75個int類型的數(shù)據(jù)項分配內(nèi)存,可以使用以下的語句:
pNumber = (int *) malloc(75*sizeof(int));
如前所述,sizeof是一個運算符,它返回一個size_t類型的無符號整數(shù),該整數(shù)是存儲它的參數(shù)需要的字節(jié)數(shù)。它把關(guān)鍵字如int或float等作為參數(shù),返回存儲該類型的數(shù)據(jù)項所需的字節(jié)數(shù)。它的參數(shù)也可以是變量或數(shù)組名。把數(shù)組名作為參數(shù)時,sizeof返回存儲整個數(shù)組所需的字節(jié)數(shù)。前一個例子請求分配足以存儲75個int數(shù)據(jù)項的內(nèi)存。以這種方式使用sizeof,可以根據(jù)不同的C編譯器為int類型的值自動調(diào)整所需的內(nèi)存空間。
試試看:動態(tài)內(nèi)存分配
下面使用指針來計算質(zhì)數(shù),將動態(tài)內(nèi)存分配的概念應(yīng)用于實踐。質(zhì)數(shù)是只能被1和這個數(shù)本身整除的整數(shù)。
查找質(zhì)數(shù)的過程非常簡單。首先,由觀察得知,2、3和5是前三個質(zhì)數(shù),因為它們不能被除了1以外更小的數(shù)整除。其他質(zhì)數(shù)必定都是奇數(shù)(否則它們可以被2整除),所以要找出下一個質(zhì)數(shù),可以從最后一個質(zhì)數(shù)開始,給它加2。檢查完這個數(shù)后,再給它加2,繼續(xù)檢查。
檢查一個數(shù)是否為質(zhì)數(shù),而不只是奇數(shù),可以用這個數(shù)除以比它小的所有奇數(shù)。其實不需要這么麻煩。如果一個數(shù)不是質(zhì)數(shù),它必定能被比它小的質(zhì)數(shù)整除。我們要按順序查找質(zhì)數(shù),所以可以把已經(jīng)找到的質(zhì)數(shù)作為除數(shù),確定所檢查的數(shù)是否為質(zhì)數(shù)。
這個程序?qū)⑹褂弥羔樅蛣討B(tài)內(nèi)存分配:
/* Program 7.11 A dynamic prime example */
#include
#include
#include int main(void)
{
unsigned long *primes = NULL; /* Pointer to primes storage area */
unsigned long trial = 0;       /* Integer to be tested */
bool found = false;            /* Indicates when we find a prime */
size_t total = 0;              /* Number of primes required */
size_t count = 0;              /* Number of primes found */printf("How many primes would you like - you'll get at least 4? ");
scanf("%u", &total); /* Total is how many we need to find */
total = total<4U ? 4U:total; /* Make sure it is at least 4 *//* Allocate sufficient memory to store the number of primes required */
primes = (unsigned long *)malloc(total*sizeof(unsigned long));
if(primes == NULL)
{
printf("Not enough memory. Hasta la Vista, baby.");
return 1;
}/* We know the first three primes */
/* so let's give the program a start. */
*primes = 2UL;      /* First prime */
*(primes+1) = 3UL;  /* Second prime */
*(primes+2) = 5UL;  /* Third prime */
count = 3U;         /* Number of primes stored */
trial = 5U;         /* Set to the last prime we have *//* Find all the primes required */
while(count
{
trial += 2UL;                 /* Next value for checking *//* Try dividing by each of the primes we have */
/* If any divide exactly - the number is not prime */
for(size_t i = 0 ; i < count ; i++)
if(!(found = (trial % *(primes+i))))
break; /* Exit if no remainder */if(found) /* we got one - if found is true */
*(primes+count++) = trial; /* Store it and increment count */
}/* Display primes 5-up */
for(size_t i = 0 ; i < total ; i ++)
{
if(!(i%5U))
printf("");                /* Newline after every 5 */
printf ("%12lu", *(primes+i));
}
printf("");                     /* Newline for any stragglers */
return 0;
}
程序的輸出如下:
How many primes would you like - you'll get at least 4? 25
2       3      5       7     11
13     17     19     23     29
31     37     41     43     47
53     59     61     67     71
73     79     83     89     97
代碼的說明
在這個例子中,可以輸入要程序產(chǎn)生的質(zhì)數(shù)個數(shù)。指針變量primes引用一塊用于存儲所計算的質(zhì)數(shù)的內(nèi)存區(qū)。然而,在程序中沒有一開始就定義內(nèi)存。這塊空間是在輸入質(zhì)數(shù)個數(shù)后分配的:
printf("How many primes would you like - you'll get at least 4? ");
scanf("%u", &total); /* Total is how many we need to find */
total = total<4U ? 4U:total; /* Make sure it is at least 4 */
在提示后,輸入的值存儲在total中。下一行語句確保total至少是4。這是因為程序?qū)⒍x并存儲已知的前三個質(zhì)數(shù)。
然后,使用total的值分配適當(dāng)數(shù)量的內(nèi)存來存儲質(zhì)數(shù):
primes = (unsigned long *)malloc(total*sizeof(unsigned long));
if(primes == NULL)
{
printf("Not enough memory. Hasta la Vista, baby.");
return 1;
}
質(zhì)數(shù)的大小增長得比其數(shù)量快,所以把它們存儲在unsigned long類型中。但如果要指定可以處理的最大質(zhì)數(shù),可以使用unsigned long long類型。程序把每個質(zhì)數(shù)存儲為類型long,所以需要的字節(jié)數(shù)是total*sizeof(unsigned long)。如果malloc()函數(shù)返回NULL,就不分配內(nèi)存,而是顯示一條信息,并結(jié)束程序。
可以指定最大的質(zhì)數(shù)個數(shù)取決于計算機的可用內(nèi)存和編譯器使用malloc()一次能分配的內(nèi)存量,前者是主要的限制。malloc()函數(shù)的參數(shù)是size_t類型,所以size_t對應(yīng)的整數(shù)類型限制了可以指定的字節(jié)數(shù)。如果size_t對應(yīng)4字節(jié)的無符號整數(shù),則一次至多可以分配4 294 967 295個字節(jié)。
一旦有了分配給質(zhì)數(shù)的內(nèi)存,就定義前三個質(zhì)數(shù),將它們存儲到primes指針指向的內(nèi)存區(qū)的前三個位置:
*primes = 2UL;      /* First prime */
*(primes+1) = 3UL;  /* Second prime */
*(primes+2) = 5UL;  /* Third prime */
可以看到,引用連續(xù)的內(nèi)存位置是很簡單的。primes是unsigned long類型的指針,所以primes+1引用第二個位置的地址-- 這個地址是primes加上存儲一個unsigned long類型數(shù)據(jù)項所需的字節(jié)數(shù)。使用間接運算符存儲每個值;否則就要修改這個地址本身。
有了三個質(zhì)數(shù),就把count變量設(shè)定為3,用最后一個質(zhì)數(shù)5初始化變量trial:
count = 3U; /* Number of primes stored */
trial = 5U; /* Set to the last prime we have */
開始查找下一個質(zhì)數(shù)時,給trial中的值加2,得到下一個要測試的數(shù)。所有的質(zhì)數(shù)都在while循環(huán)內(nèi)查找:
while(count
{
...
}
在循環(huán)內(nèi)每找到一個質(zhì)數(shù),就遞增count變量,當(dāng)它到達total值時,循環(huán)就結(jié)束。
在while循環(huán)內(nèi),首先將trial的值加2UL,然后測試它是否是質(zhì)數(shù):
trial += 2UL; /* Next value for checking *//* Try dividing by each of the primes we have */
/* If any divide exactly - the number is not prime */
for(size_t i = 0 ; i < count ; i++)
if(!(found = (trial % *(primes+i))))
break; /* Exit if no remainder */
for循環(huán)用于測試。在這個循環(huán)內(nèi),把trial除以每個質(zhì)數(shù)的余數(shù)存放到found中。如果除盡,余數(shù)就是0,因此found設(shè)置為false。如果余數(shù)是0,就表示trial中的值不是質(zhì)數(shù),可以繼續(xù)測試下一個數(shù)。
賦值表達式的值存儲到賦值運算符左邊的變量中。因此,表達式(found= (trial %*(primes+i))) 的結(jié)果存儲到found中。如果除盡,found就是false,表達式!(found=(trial%*(primes+i)))將是true,執(zhí)行break語句。因此,如果trial能整除任一個先前存儲的質(zhì)數(shù),for循環(huán)就會結(jié)束。
如果沒有一個質(zhì)數(shù)除trial是整除,當(dāng)所有的質(zhì)數(shù)都試過后,就結(jié)束for循環(huán),found的結(jié)果是把最后一個余數(shù)(它是某個正整數(shù))轉(zhuǎn)換為bool類型的值。如果trial能被某個質(zhì)數(shù)整除,循環(huán)會通過break語句結(jié)束,found會含有false。因此,可以在完成for循環(huán)時,使用存儲在found中的值確定是否找到一個新的質(zhì)數(shù):
if(found)                        /* we got one - if found is true */
*(primes+count++) = trial;  /* Store it and increment count */
如果found是true,就將trial的值存儲到內(nèi)存區(qū)的下一個位置上。下一個位置的地址是primes+count。第一個位置是primes,所以當(dāng)有count個質(zhì)數(shù)時,最后一個質(zhì)數(shù)所占的位置是primes+count-1。這個語句存儲了新的質(zhì)數(shù)后,遞增count的值。
while循環(huán)重復(fù)這個過程,直到找出所有的質(zhì)數(shù)為止。然后,以5個一行輸出質(zhì)數(shù):
for(size_t i = 0 ; i < total ; i ++)
{
if(!(i%5U))
printf(""); /* Newline after every 5 */
printf ("%12lu", *(primes+i));
}
printf("");     /* Newline for any stragglers */
for循環(huán)會輸出total個質(zhì)數(shù)。printf()函數(shù)在當(dāng)前行上顯示每個質(zhì)數(shù),但if語句在5次迭代后輸出一個換行符,所以每行顯示5個質(zhì)數(shù)。因為質(zhì)數(shù)的個數(shù)不會剛好是5的倍數(shù),所以在結(jié)束循環(huán)后,輸出一個換行符,以確保在輸出的最后至少有一個換行符。
7.4.3  用calloc()函數(shù)分配內(nèi)存
在頭文件中聲明的calloc()函數(shù)與malloc()函數(shù)相比有兩個優(yōu)點。第一,它把內(nèi)存分配為給定大小的數(shù)組,第二,它初始化了所分配的內(nèi)存,所有的位都是0。calloc()函數(shù)需要兩個參數(shù):數(shù)組的元素個數(shù)和數(shù)組元素占用的字節(jié)數(shù),這兩個參數(shù)的類型都是size_t。該函數(shù)也不知道數(shù)組元素的類型,所以所分配區(qū)域的地址返回為void *類型。
下面的語句使用calloc()為包含75個int元素的數(shù)組分配內(nèi)存:
int *pNumber = (int *) calloc(75, sizeof(int));
如果不能分配所請求的內(nèi)存,返回值就是NULL,也可以檢查分配內(nèi)存的結(jié)果,這非常類似于malloc(),但calloc()分配的內(nèi)存區(qū)域都會初始化為0。
將程序7.11改為使用calloc()代替malloc()來分配需要的內(nèi)存,只需修改一條語句,如下面的粗體顯示,其他代碼不變:
/* Allocate sufficient memory to store the number of primes required */
primes = (unsigned long *)calloc(total, sizeof(unsigned long));
if (primes == NULL)
{
printf(" Not enough memory. Hasta la Vista, baby. ");
return 1;
}
7.4.4  釋放動態(tài)分配的內(nèi)存
在動態(tài)分配內(nèi)存時,應(yīng)總是在不需要該內(nèi)存時釋放它們。堆上分配的內(nèi)存會在程序結(jié)束時自動釋放,但最好在使用完這些內(nèi)存后立即釋放,甚至是在退出程序之前,也應(yīng)立即釋放。在比較復(fù)雜的情況下,很容易出現(xiàn)內(nèi)存泄漏。當(dāng)動態(tài)分配了一些內(nèi)存時,沒有保留對它們的引用,就會出現(xiàn)內(nèi)存泄漏,此時無法釋放內(nèi)存。這常常發(fā)生在循環(huán)內(nèi)部,由于沒有釋放不再需要的內(nèi)存,程序會使用越來越多的內(nèi)存,最終占用所有內(nèi)存。
當(dāng)然,要釋放用malloc()或calloc()分配的內(nèi)存,必須使用函數(shù)返回的引用內(nèi)存塊的地址。要釋放動態(tài)分配的內(nèi)存,而該內(nèi)存的地址存儲在pNumber指針中,可以使用下面的語句:
free(pNumber);
free()函數(shù)的形參是void *類型,所有指針類型都可以自動轉(zhuǎn)換為這個類型,所以可以把任意類型的指針作為參數(shù)傳送給這個函數(shù)。只要pNumber包含分配內(nèi)存時malloc()或calloc()返回的地址,就會釋放所分配的整個內(nèi)存塊,以備以后使用。
如果給free()函數(shù)傳送一個空指針,該函數(shù)就什么也不做。應(yīng)避免兩次釋放相同的內(nèi)存區(qū)域,因為在這種情況下,free()函數(shù)的操作是不確定的,因此也就無法預(yù)料。如果多個指針變量引用已分配的內(nèi)存,就有可能兩次釋放相同的內(nèi)存,所以要特別小心。
下面修改前面的例子,使用calloc(),并在程序的最后釋放內(nèi)存。
試試看:釋放動態(tài)分配的內(nèi)存
這個程序使用指針和動態(tài)分配的內(nèi)存:
/* Program 7.11A Allocating and freeing memory */
#include
#include
#include
int main(void)
{
unsigned long *primes = NULL;  /* Pointer to primes storage area */
unsigned long trial = 0;       /* Integer to be tested */
bool found = false;            /* Indicates when we find a prime */
size_t total = 0;              /* Number of primes required */
size_t count = 0;              /* Number of primes found */
printf("How many primes would you like - you'll get at least 4? ");
scanf("%u", &total); /* Total is how many we need to find */
total = total<4U ? 4U:total; /* Make sure it is at least 4 */
/* Allocate sufficient memory to store the number of primes required */
primes = (unsigned long *)calloc(total, sizeof(unsigned long));
if (primes == NULL)
{
printf(" Not enough memory. Hasta la Vista, baby. ");
return 1;
}
/* Code to determine the primes as before...*/
/* Display primes 5-up */
for(int i = 0 ; i < total ; i ++)
{
if(!(i%5U))
printf(" "); /* Newline after every 5 */
printf ("%12lu", *(primes+i));
}
printf(" "); /* Newline for any stragglers */
free(primes); /* Release the memory */
return 0;
}
如果輸入相同,這個程序的輸出與上一個版本相同。只要兩行粗體顯示的代碼與上一個版本不同。程序現(xiàn)在使用calloc()來分配內(nèi)存,該函數(shù)的第一個參數(shù)是long類型的字節(jié)數(shù),第二個參數(shù)是total,即需要的質(zhì)數(shù)個數(shù)。在結(jié)束程序的return語句之前,用primes作為參數(shù)調(diào)用free()函數(shù),釋放了分配的內(nèi)存。
7.4.5  重新分配內(nèi)存
realloc()函數(shù)可以重用前面通過malloc()或calloc() (或realloc())分配的內(nèi)存。函數(shù)需要兩個參數(shù):一個是指針,它包含前面調(diào)用malloc()、calloc()或realloc()返回的地址,另一個是要分配的新內(nèi)存的字節(jié)數(shù)。
realloc()函數(shù)釋放第一個指針參數(shù)引用的之前分配的內(nèi)存,然后重新分配該內(nèi)存區(qū)域,以滿足第二個參數(shù)指定的新請求。顯然,第二個參數(shù)的值不應(yīng)超過以前分配的字節(jié)數(shù)。否則,新分配的內(nèi)存將與以前分配的內(nèi)存區(qū)域大小相同。
下面的代碼演示了如何使用realloc()函數(shù):
long *pData = NULL;     /* Stores the data */
size_t count = 0;       /* Number of data items */
size_t oldCount = 0;    /* previous count value */
while(true)
{
oldCount = count;     /* Save previous count value */
printf("How many values would you like? ");
scanf("%u", &count);  /* Total is how many we need to find */if(count == 0)        /* If none required, we are done */
{
if(!pData)          /* If memory is allocated */
free(pData);       /* release it */
break;              /* Exit the loop */
}/* Allocate sufficient memory to store count values */
if((pData && (count <= oldCount) /* If there's big enough old memory... */
pData = (long *)realloc(pData, sizeof(long)*count); /* reallocate it. */
else
{                    /* There wasn't enough old memory */
if(pData)           /* If there's old memory... */
free(pData);       /* release it. *//* Allocate a new block of memory */
pData = (long *)calloc(count, sizeof(long));
}
if (pData == NULL)   /* If no memory was allocated... */
{
printf("Not enough memory.");
return 1;          /* abandon ship! */
}
/* Read and process the data and output the result... */
}
很容易通過注釋理解這段代碼。循環(huán)讀取任意個由用戶提供的數(shù)據(jù)項,如果以前分配過內(nèi)存空間,且該空間足以滿足新請求,就再次使用該空間。如果以前沒有分配過內(nèi)存空間,或空間不夠大,代碼就使用calloc()分配一塊新內(nèi)存。
從這段代碼中可以看出,重新分配內(nèi)存需要做許多工作,因為一般需要確保已有的內(nèi)存塊足以滿足新請求。在大多數(shù)情況下,最好明確釋放舊內(nèi)存塊,再分配一塊全新的內(nèi)存。
下面是使用動態(tài)分配的內(nèi)存的基本規(guī)則:
●避免分配大量的小內(nèi)存塊。分配堆上的內(nèi)存有一些系統(tǒng)開銷,所以分配許多小的內(nèi)存塊比分配幾個大內(nèi)存塊的系統(tǒng)開銷大。
●僅在需要時分配內(nèi)存。只要使用完堆上的內(nèi)存塊,就釋放它。
●總是確保釋放已分配的內(nèi)存。在編寫分配內(nèi)存的代碼時,就要確定在代碼的什么地方釋放內(nèi)存。
●在釋放內(nèi)存之前,確保不會無意中覆蓋堆上分配的內(nèi)存的地址,否則程序就會出現(xiàn)內(nèi)存泄漏。在循環(huán)中分配內(nèi)存時,要特別小心。
本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
C語言的那些小秘密之內(nèi)存分配
一份通俗易懂的C語言內(nèi)存總結(jié)
calloc與malloc的區(qū)別
處理動態(tài)鏈表所需的函數(shù)
c與c++分別是怎樣動態(tài)分配和釋放內(nèi)存的,有什么區(qū)別?(轉(zhuǎn)) demo大全
malloc()與calloc區(qū)別
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服