C的變量類型、作用域與生命周期的總結
最近在看“C Programing Language" (Kernighan, Ritchie)關于外部變量的讨論,之前在學C的時候對這些extern, auto, static, register等不是太了解,這本書講的很詳細,現在總結一下。
首先, C的變量分成局部變量 local variable 和全局變量 global variable。
【注】
C 中局部(local)變量(也有翻譯成本地變量),也可以叫做内部(internal)變量
C 中全局(global)變量,又可叫外部(external)變量。
這些稱呼都可以互換,不同的稱呼可能強調的是不同的特性,以下盡量用對應使用的關鍵字來稱呼,比如局部變量将用自動變量(auto)來稱呼,全局變量将用外部變量(extern)來稱呼。
C語言程式可以看成是由許多外部對象構成的,這些外部對象可以是變量或函數。外部(external)和内部(internal)是相對的,internal是用來描述在函數内部的函數參數或變量,external描述的是定義在函數外部的變量。由于C語言不允許在函數内部定義函數,是以函數都可以看成是外部的(extern)。
栗子:
include
define MAX 100
char s[MAX];
void printString(void)
{
printf("%s", s);
}
int main(void)
scanf("%s", s);
printString(s);
return 0;
這個簡單的printString.c源檔案即可看成是由三個外部對象構成:外部變量s, 外部函數printString()和main()組成,還有一個include的标準庫檔案stdio.h(這個下次再談)。
預設情況下,外部變量與函數具有以下性質:通過同一個名字引用的所有外部變量(即使這種引用來自單獨編譯的不同函數)實際上都是引用記憶體中同一個對象。
因為外部變量可以在全局範圍内通路,這就為函數之間的資料交換提供一種新的方式,可以代替函數參數與傳回值,如上一個栗子。任何函數都可以通過名字通路一個外部變量,當然這個名字需要通過某種方式來聲明。
外部變量與自動變量的作用域與生命周期
變量或者符号的作用域是指程式中可以使用這個變量或名字的範圍。這個可以看成是靜态的代碼範圍
變量生命周期則是指該變量存在的時間範圍。這個可以了解為程式運作時的變量存在的時間周期。
自動變量隻能在函數内部使用,作用域從聲明處開始直至函數結束,生命周期是從其所在的函數被調用時,變量開始存在,在函數退出時變量将消失。對于在函數開頭聲明的自動變量,其作用域即為聲明該變量名的函數内部,函數的參數也是如此,實際上可以将它看作是這個函數的局部變量。當然,自動變量也可以定義在函數内的語句塊中,比如在下面的for循環中定義的臨時變量temp:
int func(void)
int i;
for(i = 0; i < 10; i++)
{
int temp;
...
}
這種變量當然作用域是限定在語句塊内部,生命周期也是在該語句塊内部,當程式執行完該語句塊,變量也就消失了。
外部變量作用域為從其定義處開始直至所在的檔案的結尾結束,生命周期是永久存在的,即程式執行期間一直存在,它們的值在一次函數調用結束到下一次函數調用開始之前保持不變。另一方面,可以通過添加聲明的方式來使用外部變量,按照上面所說,外部變量是唯一的,是以添加extern聲明可以擴充外部變量的作用域到其他檔案中。
如果要在外部變量的定義之前使用該變量,或者外部變量的定義與變量的使用不在同一個檔案中,則必須在相應的變量聲明中強制使用關鍵字extern。
将外部變量的定義與聲明差別開是很有必要的,外部變量的聲明用于說明變量的屬性(主要是類型),而外部變量的定義除此之外還會引起記憶體的配置設定(在定義後編譯程式将為它配置設定記憶體單元)。
而自動變量則不然,自動變量在C中沒有定義這一說法,隻要先聲明再使用即可,這是因為自動變量(即局部變量)是在運作時由棧來管理的,而外部變量(即全局變量)是在編譯過程中由編譯器、彙編器配置設定存儲位址,一直到連結時确定記憶體位置(這些内容将在之後會專門總結有關編譯、彙編、連結的内容),在這裡都可以了解為在編譯時即配置設定了記憶體單元。
栗子:如果将下面這兩條語句放在所有函數的外部:
int a;
double b[MAX];
則這兩條語句将定義外部變量a與b,并為之配置設定記憶體,同時這兩條語句還可以作為該源檔案中其餘部分的聲明。而下面的兩行語句:
extern int a;
extern double b[];
為所在檔案該語句之後的部分聲明了一個int類型的外部變量a以及一個double類型的外部變量b(該數組的長度在其他地方确定),但這兩個聲明并沒有建立變量或為他們配置設定記憶體。
在程式的源檔案中,一個外部變量隻能在某個檔案中定義一次,而其他檔案可以通過extern聲明來通路它(定義外部變量的源檔案中也可以包含對該外部變量的extern聲明)。外部變量的定義必須指定數組的長度,但extern聲明則不一定要指定數組的長度。外部變量的初始化隻能出現在其定義中(注:若外部變量未初始化,編譯器将它初始化為0)。
【注】外部變量的聲明也可以通過上下文隐式聲明(即如上所說,定義即可作為之後語句的聲明)如下面程式版本2中的外部變量len和buf在main函數中就無需在main中再聲明。
版本1:
define MAXLENGTH 1000 // buffer最大長度
char buf[MAXLENGTH];
int getline(void);
/*一個簡單的copy-paste程式
*/
while (getline()!=EOF)
{
printf("%s\n", buf);
}
return 0;
int getline(void)
int c, i;
extern char buf[MAXLENGTH];
i= 0;
while ( i < MAXLENGTH && (c = getchar()) != EOF && c != '\n')
buf[i++] = c;
if (c = '\n')
buf[i++] = c;
buf[i] = '\0';
return i;
版本2:
while (getline()!=EOF)
{
printf("%s\n", buf);
}
return 0;
int c, i;
// 通過上下文隐式聲明 buf[]
i= 0;
while ( i < MAXLENGTH && (c = getchar()) != EOF && c != '\n')
buf[i++] = c;
if (c = '\n')
buf[i++] = c;
buf[i] = '\0';
return i;
靜态變量與寄存器變量
靜态變量
之前已經提到了,外部變量與自動變量,其中外部變量是可以被全局使用的,這個全局指的是整個源程式的所有源檔案都可以通過添加extern聲明來使用。但是,如果我們希望限定這個外部變量僅限于該定義的源檔案使用,而不希望被其他源檔案使用。那我們可以使用static聲明限定外部變量和函數,可以将其聲明的對象的作用域限定為該源檔案的剩餘部分。通過static限定外部對象,可以達到隐藏外部對象的目的。
static char buf[BUFSIZE];
static int bufp = 0;
變量聲明為static之後,該變量即為靜态存儲,其他檔案中的函數就不可以通路變量buf, bufp,是以這兩個名字就不會和同一程式中的其他檔案中相同名字的變量相沖突。
【注】:多個函數中的自動變量同名,也不會造成沖突,因為在編譯過程中,編譯器會将自動變量改成不同名字,比如加上函數名,具體做法依賴于編譯器版本。
外部的static聲明多用于變量,當然,也可以用于聲明函數。通常情況下,函數名是全局可通路的,對整個程式的各個部分都是可見的。但是,如果把函數聲明為static類型,則該函數除了對該函數聲明所在的檔案可見外,其他檔案都無法通路。
static也可用于聲明自動變量,static類型的自動變量同一般的自動變量一樣,是某個特定函數内的局部變量,隻能在該函數中使用。但它與一般的自動變量不同的是,不管其所在函數是否被調用,它一直存在。換句話說,static類型的内部變量作用域不變,生命周期和外部變量一樣為整個程式運作期間。
寄存器變量
register聲明告訴編譯器,它所聲明的變量在程式中使用頻率較高。其思想史,将register變量放在機器的寄存器中,這樣可以使程式更小,執行速度更快。
register聲明的形式如下:
register int x;
register char c;
register聲明隻适用于自動變量以及函數的形式參數,看下面的例子:
void f(register unsigned a, register long n)
register int i;
...
實際使用的時候,底層硬體環境會對寄存器變量的使用有一些限制。每個函數中隻有很少的變量可以儲存在寄存器中,且隻允許某些類型的變量。但是,過量的寄存器變量并沒有什麼害處,因為編譯器可以忽略過量的或不支援的寄存器變量聲明。另外,無論寄存器變量實際上是不是存放在寄存器中,它的位址都是不能通路的。
總結
變量類型 定義 作用域 生命周期 說明
自動變量 定義在函數内部或者是函數參數 自聲明其至函數結尾或者是所在語句塊結尾 作用域生效則生效,作用域失效則失效,多次調用,重新建立該變量 在運作時,由棧管理
外部變量 定義在函數外部或者是函數 自定義起至所在檔案結尾,可通過extern聲明,擴充至全局 整個程式運作期間 在編譯時,一旦定義即建立變量、配置設定記憶體
靜态變量 聲明時使用,通過添加static來聲明 聲明外部變量時,該外部變量作用域僅為聲明所在檔案,聲明自動變量時,不改變 整個程式運作期間 可以限定全局變量,函數或者是自動變量
寄存器類型 通過添加register聲明 - - 隻能限定自動變量或函數參數,可能被存放在寄存器中,也可能被忽略,但是被聲明為寄存器類型的變量位址不可通路
是以,可以将變量分為:被初始化的全局範圍的外部變量,被初始化的靜态類型外部變量,未被初始化的兩類外部變量,自動變量,靜态類型自動變量,寄存器類型自動變量。
至于為什麼這麼分,下次讨論編譯的時候用的上。
作者:DeepC
出處:
https://www.cnblogs.com/deepC/