存儲時期:變量在內存中保存的時間。變量的作用域和鏈接一起表明程序的哪些部分可以通過變量名來使用該變量。
不同的存儲類提供了變量的作用域、鏈接以及存儲時期的不同組合。
12.1.1 作用域
作用域描述了程序中可以訪問一個標識符的一個或多個區域。一個C變量的作用域可以是代碼塊作用域、函數原型作用域,或者文件作用域。
在代碼塊中定義的變量具有代碼塊作用域,從該變量被定義的地方到包含該定義的代碼塊的末尾該變量均可見。另外,函數的形式參量盡管在函數的開始花括號前進行定義,同樣也具有代碼塊作用域,隸屬於包含函數體的代碼塊。
傳統上,具有代碼塊作用域的變量都必須在代碼塊的開始處進行聲明。C99放寬了這一限制,允許在一個代碼塊中任何位置聲明變量。
函數原型作用域從變量定義處一直到原型聲明的末尾。這意味著編譯器在處理一個函數原型的參數時,它所關心的只是該參數的類型,而不關注參數名字。(除VLA外)
一個所有函數之外定義的變量具有文件作用域,它從定義處到包含該定義的文件尾都是可見的。
另外還有一種被稱為函數作用域的作用域,但它只適用於goto語句使用的標簽。
12.1.2 鏈接
一個C變量具有下列鏈接之一:外部鏈接、內部鏈接或空鏈接。
具有代碼塊作用域或者函數原型作用域的變量具有空鏈接,意味著它們是由其定義所在的代碼塊或函數原型所私有的。
具有文件作用域的變量可能有內部或者外部鏈接。
一個具有外部鏈接的變量可以在一個多文件程序的任何地方使用。
一個具有內部鏈接的變量可以在一個文件的任何地方使用。
12.1.3 存儲時期
如果一個變量具有靜態存儲時期,它在程序執行期間將一直存在。具有文件作用域的變量具有靜態存儲時期。
所有的文件作用域變量,無論它具有內部鏈接,還是具有外部鏈接,都具有靜態存儲時期。
12.1.4 自動變量
默認情況下,在代碼塊作用域或函數的頭部定義的任意變量都屬於自動存儲類。也可以通過顯示的使用關鍵字auto使您的這個意圖更清晰。
語句若為循環或者if語句的一部分,即使沒有使用{},也認為是一個代碼塊。
12.1.5 寄存器變量
聲明一個寄存器類變量僅是一個請求,而非一條直接的命令。所以可能達不成目的,而成為一個普通的自動變量。
可以使用register聲明的類型是有限的。例如,處理器可能沒有足夠大的寄存器來容納double類型。
12.1.6 具有代碼塊作用域的靜態變量
靜態變量和外部變量在程序調入內存時已經就位了。
12.1.7 具有外部鏈接的靜態變量
把變量的定義聲明放在所有函數之外,即創建了一個外部變量。為了程序更加清晰,可以使用extern關鍵字來再次聲明它。如果變量是在別的文件中定義的,使用extern來聲明改變量是必須的。
如果在函數的聲明中漏掉了extern,就會建立一個獨立的自動變量。
外部變量的作用域:從聲明的位置開始到文件結尾為止。
和自動變量一樣,外部變量可以被顯示地初始化。不同於自動變量的是,如果您不對外部變量進行初始化,它們被自動賦值0。
main()
{
external int tern;
第一次聲明tern稱為定義聲明,第二次聲明稱為引用聲明。
不要用關鍵字extern來進行外部定義,只用它來引用一個已經存在的外部定義。
一個外部變量只可進行一次初始化,而且一定是在變量被定義時進行的。
extern char permis='u'; //錯誤
12.1.8 具有內部鏈接的靜態變量
它只可以被與它在同一個文件中的函數使用。
C語言中有5個作為存儲類說明符的關鍵字,它們是auto、register、static、extern以及typedf。typedf與內存存儲無關,由於語法原因被歸入此類。不可以在一個聲明者使用一個以上存儲類說明符。
auto:一個變量具有自動存儲時期,該說明符只能用在具有代碼塊作用域的變量聲明中。
register:也只能用在具有代碼塊作用域的變量聲明中,它的使用也使您不能獲得變量的地址。
static:用於具有代碼塊作用域的變量聲明時,使該變量具有靜態存儲時期,從而得以在程序運行期間存在並保留其值。用於文件作用域的變量聲明時,表明該變量具有內部鏈接。
extern:表明您在聲明一個已經在別處定義了的變量。
函數也具有存儲類,可以是外部的或者靜態的。外部函數可以被其它文件中的函數調用,而靜態函數只可以在定義它的文件中使用。
malloc():它接受一個參數:所需內存字節數,然後malloc()找到可用內存中一個大小合適的塊,返回那塊內存第一個字節的地址,如果找不到所需空間,返回空指針。
double * ptd;
ptd=(double *)malloc(30 *sizeof(double));
這段代碼請求30個double類型值的空間,並且把ptd指向該空間鎖在位置。
現在創建數組有三種方法
1、聲明一個數組,用常量表達式指定數組維數。
2、聲明一個變長數組,聲明時用變量表達式指定數組維數。
3、聲明一個指針,調用malloc()。
一般地,對應每個malloc()調用,應該調用一次free()。函數free()的參數是先前malloc()返回的地址,不能使用free()來釋放通過其它方式分配的內存。
在頭文件stdlib.h有malloc()和free()的原型。
標准庫提供了兩個保證能夠在所有操作系統下工作的返回值:EXIT_FAILURE指示程序異常終止,EXIT_SUCCESS指示程序正常終止。
12.4.1 free()的重要性
在編譯程序時,靜態變量的數量是固定的,在程序運行時也不改變。自動變量使用的內存數量在程序執行時自動增加或減少,但被分配的內存所使用內存數量只會增加,除非您記得使用free()。否則可能造成內存洩露。
12.4.2 函數calloc()
例子如下:
long * newmem;
newmem=(long *)calloc(100,sizeof(long));
函數calloc()還有一個特性:它將塊中的全部位都置為0。
函數free()也可以用來釋放由calloc()分配的內存。
12.4.3 動態內存分配與變長數組
VLA與malloc()都可以用來創建一個大小在運行時決定的數組。
區別在於VLA是自動存儲的,即VLA所用內存空間在運行完定義部分之後會自動釋放。
使用由malloc()創建的數組不必局限在一個函數中。
自動變量:將這一部分內存處理為一個堆棧,這意味著在內存中,新變量在創建時按順序加入,在消亡時按相反順序移除。
C99授予類型限定詞一個新屬性:它們現在是冪等的。這意味著可以在一個聲明中不止一次地使用同一限定詞,多余的將被忽略掉。
const const const int n=6;相當於const int n=6;
如果變量聲明中帶有關鍵字const,則不能通過賦值、增量或減量運算符來修改值。
12.5.1 類型限定詞volatile
限定詞volatile告訴編譯器該變量除了可被程序改變以外還可被其他代理改變。典型的,它被用於硬件地址和與其它並行運行的程序共享的數據。
一個值可以同時是const和volatile。程序不能改變它的值,這一點使它成為const,但它被程序以外的代理改變,這使它成為volatile。
12.5.1 類型限定詞restrict
關鍵字restrict只可用於指針,並表明指針是訪問一個數據對象的惟一且初始的方式。
int ar[10];
int *restrict restar=(int *)malloc(10 *sizeof(int));
int *par=ar;
指針restar是訪問由malloc()分配的內存的惟一且初始的方式,因此它可以使用這個關鍵字。
par指針既不是初始的,也不是訪問數組ar中數據的惟一方式,因此不可以使用。
void * memcpy(void * restrict s1,void * restrict s2,size_t n);
把s1,s2聲明為restrict意味著每個指針都是相應數據的惟一訪問方式,因此它們不能訪問同一數據塊,即不能重疊。