小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

動態(tài)內(nèi)存分配

 @IT小小鳥@ 2012-02-20


7.4  內(nèi)存的使用

指針是一個非常靈活且強大的編程工具,有非常廣泛的應(yīng)用。大多數(shù)C程序都在某種程度上使用了指針。C語言還進一步增強了指針的功能,為在代碼中使用指針提供了很強的激勵機制,它允許在執(zhí)行程序時動態(tài)分配內(nèi)存。只有使用指針,才能動態(tài)分配內(nèi)存。

第5章的一個程序計算一組學生的平均分,當時它只處理10個學生。假設(shè)要編寫一個程序,但事先不知道要處理多少個學生,若使用動態(tài)內(nèi)存分配(dynamic memory allocation),所使用的內(nèi)存就不會比指定的學生分數(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)換成適當?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í)行適當?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的值分配適當數(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變量,當它到達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是整除,當所有的質(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,所以當有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ù)在當前行上顯示每個質(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)存泄漏。當動態(tài)分配了一些內(nèi)存時,沒有保留對它們的引用,就會出現(xiàn)內(nèi)存泄漏,此時無法釋放內(nèi)存。這常常發(fā)生在循環(huán)內(nèi)部,由于沒有釋放不再需要的內(nèi)存,程序會使用越來越多的內(nèi)存,最終占用所有內(nèi)存。

當然,要釋放用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ǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多