C中,object指一塊內存區域,一個object可以存儲一或多個值。一個object可能還未存儲任何值,但是它有存儲一個恰當值的合適大小。"int entity =3;"聲明瞭一個叫做entity的identifier。identifier是一個名字,它指定一個具體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 linkage:external linkage的變量可用在一個多文件的程序中的任意位置
2. internal linkage:internal linkage的變量可以用在一個translation unit中的任何位置
3. no linkage:block scope, function scope和function prototype scope的變量是no linkage,意味着它們只是他們被定義的block, function或function prototype的私有變量
file scope的變量可以是internal linkage或external linkage。通常"file scope with internal linkage"變量被稱爲"file scope"變量,而"file scope with external linkage"的變量被稱爲"global scope"或"program scope"。file scope變量默認是external linkage,要想將其設爲internal linkage需要在聲明變量時加上"static"
scope和linkage描述了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 scope,no linkage和static storage duration。VLA有一點不同是他們從被聲明時纔開始存在
4. allocated storage duration
由程序員決定變量的duration(malloc()- free())
Storage Class:
1. automatic
屬於automatic storage class的變量有automatic storage duration,block scope和no linkage。任何在block或function中定義的變量默認都是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 scope,no linkage和automatic storage duration。要聲明一個register變量需要關鍵詞"register",但即便使用了關鍵詞也不一定能獲得一個register變量,compiler會決定是否給予一個register變量,如果compiler忽略了這個請求,將只能獲得一個普通automatic變量,但依然無法使用地址操作符來處理它
3. 有block scope的static變量(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 linkage的static變量(static variables with external linkage)
這類變量具有file scope,external linkage和static 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 linkage的static變量(static variables with external linkage)
這類變量具有file scope,internal linkage和static 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. VLA是automatic storage,當程序離開定義VLA的block時,內存就被釋放了
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一個變量的值可以被除程序之外的東西改變(如一個存儲了當前時間的變量可以被系統改變),以限制compiler對cache等的優化(如果沒有volatile,compiler會假定這個變量的值不會被程序之外的東西改變,進而會進行優化)。一個變量可以既是const又是volatile的,const指示表示不能被程序改變
3. restrict
主要是爲了通過給compiler更多權限來進行優化,它只能用於指針,表示該指針是訪問數據object初始且唯一的方式
4. _Atomic(C11)
要包含"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使用這個信息進行代碼優化