C的存儲類,鏈接與內存管理(Storage Class, Linkage, Memory Management)

C中,object指一塊內存區域,一個object可以存儲一或多個值。一個object可能還未存儲任何值,但是它有存儲一個恰當值的合適大小。"int entity =3;"聲明瞭一個叫做entityidentifieridentifier是一個名字,它指定一個具體object的內容,這個identifier就是C程序指定存儲在硬件內存中object的方式。變量名並不是指定一個object的唯一方式,例如"int* p = &entity;"中,p就是一個identifier;這裏*p不是identifier因爲它不是一個名字(雖然它也指定了一個object)。對數組來說,數組整體是一個object,同時其中的每一個元素也是object

C預處理器在處理#include "*.h"時,會將所包含的頭文件的內容放在include處,所以雖然有多個文件,編譯器只看到了一個文件,這一個文件就叫translation unit


只能用常量表達式來初始化file scope的變量(只要不是變長數組,sizeof表達式也被認爲是常量)



Linkage(鏈接):

1. external linkageexternal linkage的變量可用在一個多文件的程序中的任意位置

2. internal linkageinternal linkage的變量可以用在一個translation unit中的任何位置

3. no linkage:block scope, function scopefunction prototype scope的變量是no linkage,意味着它們只是他們被定義的block, functionfunction prototype的私有變量

file scope的變量可以是internal linkageexternal linkage。通常"file scope with internal linkage"變量被稱爲"file scope"變量,而"file scope with external linkage"的變量被稱爲"global scope""program scope"file scope變量默認是external linkage,要想將其設爲internal linkage需要在聲明變量時加上"static"

scopelinkage描述了identifier的可見性



Storage Duration

描述了object可以被identifier訪問的持久性

1. static storage duration:在整個程序執行過程中一直存在。file scope變量是static storage duration。用"static"聲明的file scope變量有internal linkage,但所有的file scope變量,無論是使用internal linkage還是external linkage都有static storage duration;注意,這裏關鍵詞"static"表明的是變量的linkage類型,而非storage類型static內存的使用量是在compile時決定的,static數據和程序一起同時被載入內存。static的變量不被存放在stack中,他們被存儲在data segment中。

如果static變量未初始化,被放在uninitialized data segment中;如果已初始化,則存放在initialized data segment中(所有的gloabl,static和constant data都被存儲在這個區域)。注意,stack,heap還有data segment都是在memory中的,只不過劃分了不同的區域 

2. thread storage duration:從被聲明時開始,存在到thread結束。這種object的聲明方法與file scope相同,但是要加上關鍵字"_Thread_local",當有這個關鍵字時,每個線程都會有一份這個變量的私有副本

3. automatic storage duration:當程序進入automatic storage duration的變量被定義的block時,這些變量會被分配存儲空間,當結束block時內存被釋放。block scope的變量通常具有automatic storage duration,但也可以通過添加關鍵詞"static"將其變成static storage duration,此時它們具有block scopeno linkagestatic storage durationVLA有一點不同是他們從被聲明時纔開始存在

4. allocated storage duration

由程序員決定變量的durationmalloc()- free()



Storage Class

1. automatic

屬於automatic storage class的變量有automatic storage durationblock scopeno linkage。任何在blockfunction中定義的變量默認都是automatic storage duration,但可以通過添加關鍵詞"auto"顯式聲明(但是由於C++"auto"的含義完全不同,所以爲了C/C++兼容性最好不要使用"auto")。"auto"只能用於block scope變量的聲明,而block scope的變量已經是automatic storage duration了,所以它的主要目的是提醒意圖

數組是automatic storage class,所有automatic storage class的變量都是被放在stack中的

2. register

通常變量是存儲在計算機內存中的,但register變量存儲在CPU register中(最快的可用存儲區域)。但是由於變量是存儲在register中的,所以無法取得register變量的地址。其他方面register變量與automatic變量一樣,有block scopeno linkageautomatic storage duration。要聲明一個register變量需要關鍵詞"register",但即便使用了關鍵詞也不一定能獲得一個register變量,compiler會決定是否給予一個register變量,如果compiler忽略了這個請求,將只能獲得一個普通automatic變量,但依然無法使用地址操作符來處理它

3. block scopestatic變量(static variables with block scope)(static with no linkage

這裏的static指變量一直存放於內存中(即static storage duration),其值可能改變,但是no linkage(file scope的變量自動獲得static storage duration)。這種變量只能被初始化一次,如果沒有顯式初始化則會被初始化爲0

例:

for(int i = 0; i < 3; i++)
    trystat();
...
void trystat(void)
{
    int fade = 1;
    static int stay = 1;
    printf("fade= %d, stay = %d\n", fade++, stay++);
}

結果爲:

fade= 1, stay = 1

fade= 1, stay = 2

fade= 1, stay = 3

因爲"stay"被聲明爲static storage duration,所以在程序運行過程中一直存在,而不像"fade"被聲明又被銷燬;同時"stay"只會被初始化一次,即在trystat()compile時。如果不顯式的將其初始化,則它會自動被初始化爲0。其實"staticint stay =1;"並不是trystat()函數的一部分,static變量和external變量在程序被載入內存時已經存在了,將聲明語句放在trystat()函數中只是告訴compiler這個變量只有trystat()函數可見,而不是一個在程序運行中被執行的語句

4. external linkagestatic變量(static variables with external linkage

這類變量具有file scopeexternal linkagestatic storage duration。這種類別類別也被成爲external storage class,屬於這種類別的變量叫做external變量。將變量的聲明放在任何函數外就可以創建一個external變量;同時通過使用關鍵詞"extern"可以在函數中聲明external變量(其實是引用別處已定義的external變量)。一個external變量只能被初始化一次;如果不顯式初始化,external變量(或數組)會被自動初始化爲0

如果某個external變量在一個源代碼文件中被定義,而在其他的源代碼文件中被使用,則這些其他文件必須在聲明這個變量時使用"extern"。如果file scope的變量有關鍵詞"extern",則被引用的原變量必須有external linkage;如果block scope的變量有關鍵詞"extern",則被引用的原變量必須有internal linkage或external linkage

例:extern int x; int main(void) ...

compiler會認爲變量"x"真正的定義在程序的其他位置,可能是另一個文件,所以並不會給x分配空間。因此,不要使用"extern"來創建external定義,只能用它來引用一個已經存在的external定義

5. internal linkagestatic變量(static variables with external linkage

這類變量具有file scopeinternal linkagestatic storage duration,可以通過將變量的聲明放在任何函數外並添加關鍵詞"static"來創建,只能被同一個transition unit中的函數使用。它也同樣只能在compile時被初始化一次,如果不顯示初始化則初始化爲0



C語言有6個storage class分類符:auto, register, static, exter, _Thread_local和typedef。在大多數情況下在一個storage class的聲明中不能使用一個以上的分類符,這意味着不能在typedef中使用storage class分類符。唯一的例外是_Thread_local可以和static或extern同時使用

static用法:

1. 當用於file scope變量時,表示將變量聲明爲internal linkage,只能用於一個translation unit

2. 當用於block scope變量時,表示將變量聲明爲static storage duration,變量將在程序運行過程中一直存在



函數可以是external(默認)也可以是static,如果想讓一個函數只能在一個translation unit中使用,則可以在聲明時使用關鍵詞"static";若函數的定義在其他位置,則需要通過"extern"來聲明(這些都和變量的用法一樣)



malloc()

malloc()函數只有一個參數——程序員需要的內存byte數;malloc()分配了空間但並未給空間分配名字,但它會返回所獲得空間第一個byte的地址,因此可以通過將這個地址賦給一個指針而使用它。malloc()的返回值類型是pointer-to-void的指針,這種指針是一個泛化指針,可以被賦給任何類型的指針。如果malloc()找不到要求的空間,會返回一個空指針

例:double* p; p = (double*)malloc(30 * sizeof(double)); free(p)

free()的參數應該是malloc()返回的那個指針,它只能釋放malloc()分配的空間,不能釋放通過其他方式獲得的空間。在Linux下使用free()時發現,它的作用只是告知系統p所指向的空間可以使用,然後會將其置爲0,但是,注意,p的值並沒有被置爲NULL,並且因爲p仍然指向原來的地址,甚至可以使用p去給那個空間賦值



calloc()

作用與malloc相同,但會將所分配的空間中所有的bit設爲0(但在有些硬件系統中,浮點數的0並不代表0)。它所分配的空間也需要通過free()來釋放

例:double* p; p = (double*)calloc(30, sizeof(long));



動態內存分配與變長數組(VLA):

相同:都可以在程序運行時決定所創建數組的大小

不同:
1. VLAautomatic storage,當程序離開定義VLAblock時,內存就被釋放了

2. malloc()所創建數組的訪問不被侷限在一個函數中,例如一個函數可以創建一個數組並返回指針,可以使調用它的函數使用這個數組(注意不要free兩次)

3. VLA處理多維數組更簡單




使用頭文件的好處:

不必記憶在哪個文件中進行定義,在哪個文件中進行調用(要使用extern),所有的文件只要都包含同一個頭文件即可

壞處:

主要是變量的重複,如:

/*1.h */ static const double PI = 3.14;

/*2.h */ #include "1.h"

在這種情況下,必須要使用關鍵詞static讓變量具有internal linkage,否則在每個文件中都會定義一個external"PI",是不合法的(前面提到過,#include"*.h"是把頭文件的內容放在此處)



類限定詞(type qualifier):

1. const

可以用來保護global variables

2. volatile

警告compiler一個變量的值可以被除程序之外的東西改變(如一個存儲了當前時間的變量可以被系統改變),以限制compilercache等的優化(如果沒有volatilecompiler會假定這個變量的值不會被程序之外的東西改變,進而會進行優化)。一個變量可以既是const又是volatile的,const指示表示不能被程序改變

3. restrict

主要是爲了通過給compiler更多權限來進行優化,它只能用於指針,表示該指針是訪問數據object初始且唯一的方式

4. _AtomicC11

要包含"stdatomic.h""threads.h",主要用於並行計算。當一個thread對一個atomic類型的object進行atomic的處理時,其他thread不能訪問這個object



C99允許將類限定詞和storage class限定詞"static"放在function形參列表的第一個方括號中,如

void func(int* const a, int* restrict b, int c); == void func(int a[const], int b[restrict], int c);

static產生了新的含義,如【double stick(double ar[static 20]);】表示函數調用時的實際參數會是指向至少有20個元素的數列的第一個元素的指針。這樣做的目的是能夠是compiler使用這個信息進行代碼優化


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章