C語言使用作用域、鏈接和存儲時期來定義5種存儲類:自動、寄存器、具有代碼塊作用域的靜態、具有外部鏈接的靜態,以及具有內部鏈接的靜態。
存儲類 | 時期 | 作用域 | 鏈接 | 聲明方式 |
---|---|---|---|---|
自動 | 自動 | 代碼塊 | 空 | 代碼塊內 |
寄存器 | 自動 | 代碼塊 | 空 | 代碼塊內,使用關鍵字register |
具有外部鏈接的靜態 | 靜態 | 文件 | 外部 | 所有函數之外 |
具有內部鏈接的靜態 | 靜態 | 文件 | 內部 | 所有函數之外,使用關鍵字static |
空鏈接的靜態 | 靜態 | 代碼塊 | 空 | 代碼塊內,使用關鍵字static |
我想大概可以不嚴謹的把相關概念與生活中的足球籃球運動做比較。先介紹一下背景吧,CPU、內存、硬盤分別對應比賽場、更衣室和酒店。平時數據就安安靜靜的躺在硬盤,需要計算時就來到內存做準備,整個計算過程在CPU中進行,計算結束後,數據首先回到內存,最後再返回硬盤。硬盤的數據是“靜”的,CPU/內存的數據是“動”的,其中CPU分爲計算器,控制器,寄存器和時鐘,計算器的運行就相當於正在進行激烈的比賽,寄存器就是替補席,存放隨時可能用到的數據。
介紹完背景知識,可以進行基本概念的介紹了。變量和函數都可以分成不同的存儲類,一個變量就相當於一名賽場上的運動員,函數就是戰術,沒錯,類似“牛角”、“鑽石”這樣的戰術。函數需要若干個變量來參與執行。“時期”和“作用域”分別是從時間和空間的角度進行定義的。時期分爲自動和靜態,自動意味着變量根據函數的需要靈活進行定義、使用和釋放,就像運動員執行完相應戰術,就可以下場休息了,戰術需要的時候再上場;而靜態表示變量被定義之後就一直持續到程序結束,就像運動員上場之後就一直待到比賽結束,所以說,一般情況下,自動使用較多,只有非常重要的變量才需要靜態。
作用域分爲代碼塊作用域、函數原型作用域、文件作用域與函數作用域,有些類似足球場地,整個足球場就是文件作用域,作爲全局變量的運動員擁有文件作用域,可以全場飛奔。將足球場地劃分成後場,中場和前場幾個區域,區域對應文件中的代碼塊,就像後衛一般在後場活動一樣,代碼塊作用域的變量只能在代碼塊中生效。
函數原型作用域(function prototype scope)翻譯的讓人很不容易理解,要說清楚函數原型作用域,需要先理解函數原型。函數原型(function prototype)實際上就是函數接口(function interface),聲明瞭函數的返回類型、名稱、參數類型和參數數量。將函數原型作爲函數的聲明,放在頭文件中,預處理可以使程序拆分成編譯單元,編譯器將編譯單元分別編譯彙編成object文件,再由鏈接器組合成可執行文件或者庫。關於函數原型作用域,我個人簡單粗暴的理解是:函數原型中的參數具有函數原型作用域。以下面示例爲例,函數原型中標識符“ lho”的範圍始於逗號,結束於右括號。
#include <stdio.h>
/* 函數原型 */
int simple_add (int lho, int rho);
int lho; /* 與函數原型中的 "lho" 不衝突 */
int main(void)
{
printf("%d\n", simple_add(1,2));
return 0;
}
int simple_add (int lho, int rho)
{
return (lho+rho);
}
寫了半天,函數原型作用域然並卵?額,你看那個人,他好像一條狗啊。
鏈接是從空間的角度定義的,用於引用變量或函數。鏈接分爲內鏈接、外鏈接和空鏈接。外鏈接和內鏈接比較好理解,它們的相同之處是都擁有文件作用域,定義變量或函數之後可以在本文件內任何地方進行引用;不同之處在於,外鏈接表示可以在其他文件中引用本文件的變量或函數,而內鏈接性質的變量或函數不能被其他文件引用。空鏈接表示不能被引用,代碼塊作用域的鏈接全是空鏈接,這其中“自動”和“寄存器”存儲類比較好理解,因爲調用完代碼塊,內存空間就釋放了,數據不能保存下來也就談不上引用了。具有代碼塊作用域的靜態稍微有點複雜,用打車和開車形容比較合適。靜態就是開自己的車,雖然平時車就停着不動(靜態變量一旦創建,即使代碼塊調用結束也不會消失,會持續到程序結束),但別人不能開走(無法引用具有代碼塊作用域的靜態變量,無法在其他文件中引用靜態函數),需要的時候只有本人能啓動(只有引用同一代碼塊才能使用靜態變量,只有在本地文件中引用靜態函數),而相對的,“自動”和“寄存器”就是打出租車了,因爲本來就沒有車,所以不可能把車借給別人。具有代碼塊作用域的靜態變量示例如下:
#include<stdio.h>
void trystat(void);
int main(void)
{
int count;
for(count = 1;count <= 3;count++)
{
printf("Here comes iteration %d: \n", count);
trystat();
}
return 0;
}
void trystat(void)
{
int fade = 1;
static int stay = 1;
printf("fade = %d and stay = %d\n",fade++, stay++);
}
這裏的變量stay只有使用trystat函數才能調用,如果想在其他函數中使用,只有把它的作用域從代碼塊改爲文件,即,把它改爲全局變量:
#include<stdio.h>
void trystat(void);
void printstay(void);
static int stay = 1;
int main(void)
{
int count;
for(count = 1;count <= 3;count++)
{
printf("Here comes iteration %d: \n", count);
trystat();
}
printstay();
return 0;
}
void trystat(void)
{
int fade = 1;
printf("fade = %d and stay = %d\n",fade++, stay++);
}
void printstay(void)
{
printf("This is function printstay, stay=%d\n",stay);
}
參考文檔
[1]wikipedia.Function prototype[EB/OL].https://en.wikipedia.org/wiki/Function_prototype,2020-04-29.
[2]polytechnique.function_prototype_scope[EB/OL].http://www.enseignement.polytechnique.fr/informatique/INF478/docs/Cpp/en/c/language/function_prototype_scope.html,2020-01-01.
[3]Stephen Prata.C Primer Plus[M].人民郵電出版社:北京,2014:321-335.