extern, static, const 和 inline

在使用 Objective-C 編程的時候,偶爾也會使用到 C 語言的一些特性,extern、static、const 和 inline 這四個關鍵詞是我對於其含義較爲模糊的四個關鍵詞,特寫一篇筆記用來溫習一下。算法

extern

要理解 extern 關鍵字的做用,首先要明白聲明和定義的區別。定義變量時,編譯器爲該變量分配內存,並可能還將其內容初始化爲某個值。聲明變量時,編譯器要求在其餘地方定義變量。聲明通知編譯器存在由該名稱和類型的變量,但編譯器不須要爲其分配內存,由於它是在其餘地方分配的。extern 關鍵字表示「聲明而不定義」。換句話說,它是一種顯式聲明變量或強制聲明而無需定義的方法。編程

必須在程序的一個模塊中精肯定義一個變量。 若是沒有定義或多於一個定義,則可能在連接階段產生錯誤。 變量能夠被聲明屢次,只要聲明彼此一致而且與定義一致(頭文件頗有用)。 它能夠在許多模塊中聲明,包括定義它的模塊,甚至在同一模塊中屢次。 可是在模塊中屢次聲明它一般是沒有意義的。安全

extern 變量也能夠在函數內聲明。 在這種狀況下,必須使用 extern 關鍵字,不然編譯器會將其視爲本地(自動)變量的定義,該變量具備不一樣的範圍,生命週期和初始值。 此聲明僅在函數內部可見,而不是在整個函數模塊中可見。編程語言

應用於函數原型的 extern 關鍵字絕對沒有任何內容(應用於函數定義的 extern 關鍵字固然是非語義的)。 函數原型始終是一個聲明,而不是一個定義。 此外,在標準 C 中,函數始終是外部的,但某些編譯器擴展容許在函數內定義函數。函數

Example:

File 1:優化

// Explicit definition, this actually allocates
// as well as describing
int Global_Variable;

// Function prototype (declaration), assumes 
// defined elsewhere, normally from include file. 
void SomeFunction(void);        

int main(void) {
    Global_Variable = 1;
    SomeFunction();
    return 0;
} 
複製代碼

File 2:this

// Implicit declaration, this only describes and
// assumes allocated elsewhere, normally from include
extern int Global_Variable;  

// Function header (definition)
void SomeFunction(void) {       
    ++Global_Variable;
}
複製代碼

在此示例中,變量 Global_Variable 在 File 1 中定義。爲了在 File 2 中使用相同的變量,必須聲明它。 不管文件數量多少,全局變量只定義一次; 可是,它必須在包含定義的文件以外的任何文件中聲明。spa

一般的方法是分配和實際定義進入 .c 文件,但僅僅聲明和原型不分配,只是描述類型和參數,以便編譯器能夠正常工做,而且該信息屬於 .h 頭文件,其餘人能夠安全地 include 沒有任何可能的衝突。prototype

static

在 C 編程語言(及其緊密的後代,如 C++ 和 Objective-C)中,static 是一個保留字,用於控制生命週期(做爲靜態變量)和可見性(取決於連接)。翻譯

在聲明變量或函數時做爲前綴的 static 關鍵字可能具備其餘效果,具體取決於聲明發生的位置。

Static global variable

在源文件的頂層(在任何函數定義以外)聲明爲 static 的變量僅在整個文件中可見。 在此用法中,關鍵字 static 稱爲「訪問說明符」。

Static function

相似地,靜態函數 - 在源文件的頂層(在任何類定義以外)聲明爲靜態的函數 - 僅在整個文件中可見。

Static local variables

在函數內聲明爲靜態的變量是靜態分配的,所以在整個程序執行期間保持其內存單元,同時具備與自動局部變量(auto 和 register)相同的可見範圍,這意味着保持函數的本地性。 所以,當一次調用時函數放入其靜態局部變量的任何值在再次調用函數時仍然存在。

const

在 C 語言中,const 是類型的一部分,而不是對象的一部分。例如,在 C 中,const int x = 1; 聲明一個 const int 類型的對象 x - const 是該類型的一部分,就好像它被解析爲「(const int)x」

這有兩個微妙的結果。 首先,const 能夠應用於更復雜類型的部分 - 例如,int const * const x; 聲明一個指向常量整數的常量指針,而 int const * x; 聲明一個指向常量整數的變量指針,而且 int * const x; 聲明一個指向變量整數的常量指針。 其次,由於 const 是類型的一部分,因此它必須做爲類型檢查的一部分匹配。

雖然常量在程序運行時不會更改其值,可是在程序運行時,聲明爲 const 的對象確實可能會更改其值。 一個常見的例子是嵌入式系統中的只讀寄存器,如數字輸入的當前狀態。 數字輸入的數據寄存器一般聲明爲 const 和 volatile。 這些寄存器的內容可能會在程序執行任何操做(volatile)時發生變化,但你也不該該向它們寫入(const)。

使用方式

在 C 中,全部數據類型(包括用戶定義的數據類型)均可以聲明爲 const,而 const-correctness 規定全部變量或對象都應該聲明爲這樣,除非須要修改它們。 這種對 const 的主動使用使得值「更容易理解,跟蹤和推理」,從而提升了代碼的可讀性和可理解性,使團隊工做和維護代碼更簡單,由於它傳達了有關值的預期用途的信息。 在推理代碼時,這能夠幫助編譯器和開發人員。 它還可使優化編譯器生成更高效的代碼。

簡單的數據類型

對於簡單的非指針數據類型,應用 const 限定符很簡單。 因爲歷史緣由,它能夠在類型的任何一側出現(即,const char foo ='a';等同於 char const foo ='a';)。 在某些實現中,在類型的兩側使用 const(例如,const char const)會生成警告但不會生成錯誤。

指針和引用

對於指針和引用類型,const 的含義更復雜 - 指針自己或指向的值或二者均可以是 const。此外,語法可能使人困惑。指針能夠聲明爲指向可寫值的 const 指針,或指向 const 值的可寫指針,或指向 const 值的 const 指針。 const 指針不能從新分配以指向與最初分配的對象不一樣的對象,但它可用於修改它指向的值(稱爲指針對象)。所以,引用變量是 const 指針的替代語法。另外一方面,指向 const 對象的指針能夠從新分配以指向另外一個內存位置(應該是相同類型或可轉換類型的對象),但它不能用於修改它的內存指向還能夠聲明指向 const 對象的 const 指針,既不能用於修改指針,也不能從新指定指向另外一個對象。如下代碼說明了這些細微之處:

void Foo( int * ptr,
          int const * ptrToConst,
          int * const constPtr,
          int const * const constPtrToConst )
{
    *ptr = 0; // OK: modifies the "pointee" data
    ptr  = NULL; // OK: modifies the pointer

    *ptrToConst = 0; // Error! Cannot modify the "pointee" data
    ptrToConst  = NULL; // OK: modifies the pointer

    *constPtr = 0; // OK: modifies the "pointee" data
    constPtr  = NULL; // Error! Cannot modify the pointer

    *constPtrToConst = 0; // Error! Cannot modify the "pointee" data
    constPtrToConst  = NULL; // Error! Cannot modify the pointer
}
複製代碼

遵循一般的 C 約定聲明,聲明遵循使用,而且指針中的 * 寫在指針上,表示解除引用。 例如,在聲明 int *ptr 中,解除引用的形式 *ptr是一個 int,而引用形式 ptr 是一個指向 int 的指針。 所以 const 將名稱修改成右側。

int *ptr; // *ptr is an int value
int const *ptrToConst; // *ptrToConst is a constant (int: integer value)
int * const constPtr; // constPtr is a constant (int *: integer pointer)
int const * const constPtrToConst; // constPtrToConst is a constant (pointer)
                                   // as is *constPtrToConst (value)
複製代碼

inline

在 C 和 C++ 編程語言中,內聯函數是使用關鍵字 inline 限定的函數;這有兩個目的。首先,它充當編譯器指令,建議(但不要求)編譯器經過執行內聯擴展替換函數體,即經過在每一個函數調用的地址處插入函數代碼,從而節省開銷一個函數調用。在這方面,它相似於寄存器存儲類說明符,它相似地提供了一個優化提示。內聯的第二個目的是改變連接行爲;這個細節很複雜。這是必要的,由於 C/C++ 單獨的編譯+連接模型,特別是由於函數的定義(主體)必須在使用它的全部轉換單元中重複,以容許在編譯期間內聯,若是函數具備外部連接,在連接過程當中致使衝突(它違反了外部符號的惟一性)。

內聯函數能夠用 C 或 C++ 編寫,以下所示:

static inline void swap(int *m, int *n)
{
  int temp = *m;
  *m = *n;
  *n = temp;
}
複製代碼

而後,聲明以下:

swap(&x, &y);
複製代碼

能夠被翻譯成(若是編譯器決定進行內聯,一般須要啓用優化):

int temp = x;
x = y;
y = temp;
複製代碼

當實現執行大量交換的排序算法時,這能夠提升執行速度。

相關文章
相關標籤/搜索