首頁 > 軟體

C++中4種管理資料記憶體的方式總結

2022-09-26 14:05:59

根據用於分配記憶體的方法,C++中有3中管理資料記憶體的方式:自動儲存、靜態儲存和動態儲存(有時也叫做自由儲存空間或堆)。在存在時間的長短方面,以這三種方式分配的資料物件各不相同。下面簡要介紹這三種型別。

注:C++11中新增了第四種型別——執行緒儲存

C++的4種管理資料記憶體的方式

自動儲存

在函數內部定義的常規變數使用自動儲存空間,被稱為自動變數(automatic variable),這意味著它們在所屬的函數被呼叫時自動產生,在該函數結束時消亡。例如,當在一個自定義的函數getname()中定義了一個temp陣列時,temp陣列僅當getname()函數活動時存在。當成許控制權回到main()時,temp使用的記憶體將自動被釋放。如果getname()返回temp的地址,則main()中的name指標指向的記憶體將很快得到重新使用。這就是在getname()中使用new的原因之一。

實際上,自動變數是一個區域性變數,其作用域為包含它的程式碼塊。程式碼塊是被包含在花括號中的一段程式碼。

自動變數通常儲存在棧中。這意味著執行程式碼塊時,其中的變數將依次加入到棧中,而在離開程式碼塊時,將按相反的順序釋放著些變數,著被稱為後進先出(LIFO)。因此,在程式執行過程中,棧將不斷地增大和縮小。

靜態儲存

靜態儲存是整個程式執行期間都存在的儲存方式。是變數稱為靜態的方式有兩種:一種是在函數外面定義它;另一種是在宣告變數時使用關鍵字statis:

static double fee = 56.50;

在K&R C中,只能初始化靜態陣列和靜態結構,而C++ Release2.0(及後續版本)和ASNI C中,也可以初始化自動陣列和自動結構。

注:自動儲存和靜態儲存關鍵在於:這些方法嚴格地限制了變數的壽命。變數可能存在於程式的整個生命週期(靜態變數),也可能只是在特定函數被執行時存在(自動變數)。

動態儲存

new和delete運運算元提供了一種比自動變數和靜態變數更靈活的方法。它們管理了一個記憶體池,這在C++中被稱為自由儲存空間(free store)或堆(heap)。該記憶體池同用於靜態變數和自動變數的記憶體是分開的。new和delete讓您能夠在一個函數中分配記憶體,而在另一個函數中釋放它。因此,資料的宣告週期不完全收程式或函數的生命時間控制。與使用常規變數相比,使用new和delete讓程式設計師對程式如何使用記憶體有更大的控制權。然而,記憶體管理也更復雜了。在棧中,自動新增和刪除機制使得佔用的記憶體總是連續的,單new和delete的相互影響可能導致佔用的自由儲存區不連續,這使得跟蹤新分配記憶體的位置更困難。

執行緒儲存

在多執行緒程式中,所有執行緒共用程式中的變數。Linux有一全域性變數,所有執行緒都可以使用它,改變它的值。如果每個執行緒希望能單獨擁有它,那麼就需要使用執行緒儲存了。表面上看起來這是一個全域性變數,所有執行緒都可以使用它,但它的值在每一個執行緒中又是單獨儲存的。

執行緒儲存的具體用法:

1.建立一個型別為pthread_key_t型別的變數。

2.呼叫pthread_key_create()來建立該變數,該函數有兩個引數,第一個引數就是上面宣告的 pthread_key_t變數,第二個引數是一個清理函數,用來線上程釋放該執行緒儲存的時候被呼叫,該函數指標可以設成NULL,這樣系統將呼叫預設的清理函數;

3.當執行緒中需要儲存特殊值的時候,可以呼叫pthread_setspcific(),該函數有兩個引數,第一個為前面宣告的pthread_key_t變數,第二個為void*變數,這樣可以儲存任何型別的值;

4.如果需要取出所儲存的值,呼叫pthread_getspecific(),該函數的引數為前面提到的 pthread_key_t變數,該函數返回void*型別的值;

5.登出使用pthread_key_delete()函數,該函數並不檢查當前是否有執行緒正在使用,也不會呼叫清理函數,而只是釋放以供下一次呼叫pthread_key_create()使用。

下面是前面提到的函數原型:

int pthread_setspecific(pthread_key_t key, const void *value);
void* pthread_getspecific(pthread_key_t key);
int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));
int pthread_key_delete(pthread_key_t *key);

執行緒儲存例子:

#include <pthread.h>
#include <stdio.h>


static pthread_key_t thread_key;

void* thread_function(void* args)
{
    pthread_t spid = pthread_self();

    pthread_setspecific(thread_key, (void *)spid);

    pthread_t gpid = (pthread_t)pthread_getspecific(thread_key);

    printf("set: %lu, get: %lu, %sn", spid, gpid, (spid == gpid ? "equal":"not equal"));

    return NULL;
}

int main(int argc, char** argv)
{
    int i;
    pthread_t threads[5];

    pthread_key_create(&thread_key, NULL);

    for (i = 0; i < 5; ++i) {
        pthread_create(&(threads[i]), NULL, thread_function, NULL);
    }

    for (i = 0; i < 5; ++i) {
        pthread_join(threads[i], NULL);
    }

    pthread_key_delete(thread_key);

    return 0;
}

棧、堆、記憶體漏失

如果使用new在自由儲存空間(或堆)上建立變數後,沒有呼叫delete。會發生什麼?

即使包含指標的記憶體由於作用域規則和物件生命週期的原因而被釋放,在自由儲存空間上動態分配的變數或結構依然存在。

實際上將會無法存取自由儲存空間的結構,因為指向這些記憶體的指標無效。

這將導致記憶體洩露,被洩漏的記憶體在程式的整個生命週期將不可使用,這些記憶體被分配,但無法被使用。

到此這篇關於C++中4種管理資料記憶體的方式總結的文章就介紹到這了,更多相關C++資料記憶體內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com