前言
我們知道,變量是有數據類型的,用以說明它占用多大的內存空間,可以進行什么樣的操作。
除了數據類型,變量還有一個屬性,稱為“存儲類別”。存儲類別就是變量在內存中的存放區域。在進程的地址空間中, 常量區、全局數據區和棧區可以用來存放變量的值。
常量區和全局數據區的內存在程序啟動時就已經由操作系統分配好,占用的空間固定,程序運行期間不再改變,程序運行結束后才由操作系統釋放;它可以存放 全局變量、靜態變量、一般常量和字符串常量。
棧區的內存在程序運行期間由系統根據需要來分配(使用到變量才分配內存;如果定義了變量但沒有執行到該代碼,也不會分配內存),占用的空間實時改變,使用完畢后立即釋放,不必等到程序運行結束;它可以存放局部變量、函數參數等。
我們可以通過C語言中的關鍵字來控制變量的存放區域。C語言共有 4 個關鍵字用來指明變量的存儲類別: 你的字體auto(自動的)、static(靜態的)、register(寄存器的)、extern(外部的)。
知道了變量的存儲類別,就可以知道變量的生存期。通俗地講,生存期指的是在程序運行過程中,變量從創建到銷毀的一段時間,生存期的長短取決於變量的存儲類別,也就是它所在的內存區域。
本節我們只講解 auto、static 和 register 三個關鍵字,external 將在模塊化開發中講解。
auto 變量
auto 是自動或默認的意思,很少用到,因為所有的變量默認就是 auto 的。也就是說,定義變量時加不加 auto 都一樣,所以一般把它省略,不必多次一舉。
例如:
int n = 10;
與
auto int n = 10;
的效果完全一樣。
static 變量
static 聲明的變量稱為靜態變量,不管它是全局的還是局部的,都存儲在靜態數據區(全局變量本來就存儲在靜態數據區,即使不加 static)。
靜態數據區的數據在程序啟動時就會初始化,直到程序運行結束;對於代碼塊中的靜態局部變量,即使代碼塊執行結束,也不會銷毀。
注意:靜態數據區的變量只能初始化(定義)一次,以后只能改變它的值,不能再被初始化,即使有這樣的語句,也無效。
請看下面的代碼:
#include <stdio.h>
#include <stdlib.h>
int main ()
{
int result, i;
for(i = 1; i<=100; i++){
result = sum(i);
}
printf("1+2+3+...+99+100 = %d\n", result);
system("pause");
return 0;
}
int sum(int n){
// 也可以不賦初值 0,靜態數據區的變量默認初始化為 0
static int result = 0;
result += n;
return result;
}
運行結果:
1+2+3+...+99+100 = 5050
我們在 sum() 中定義了一個靜態局部變量 result,它存儲在靜態數據區,sum() 函數執行結束也不會銷毀,下次調用繼續有效。靜態數據區的變量只能初始化一次,第一次調用 sum() 時已經對 result 進行了初始化,所以再次調用時就不會初始化了,也就是說 static int result = 0; 語句無效。
靜態局部變量雖然存儲在靜態數據區,但是它的作用域僅限於定義它的代碼塊,sum() 中的 result 在函數外無效,與 main() 中的 result 不沖突,除了變量名一樣,沒有任何關系。
register 變量
一般情況下,變量的值是存儲在內存中的,CPU 每次使用數據都要從內存中讀取。如果有一些變量使用非常頻繁,從內存中讀取就會消耗很多時間,例如 for 循環中的增量控制:
int i;
for(i=0; i<1000; i++){
// Some Code
}
執行這段代碼,CPU 為了獲得 i,會讀取 1000 次內存。
為了解決這個問題,可以將使用頻繁的變量放在CPU的通用寄存器中,這樣使用該變量時就不必訪問內存,直接從寄存器中讀取,大大提高程序的運行效率。
不過寄存器的數量是有限的,通常是把使用最頻繁的變量定義為 register 的。
來看一個計算 π 的近似值的例子,求解的一個近似公式如下:
為了提高精度,循環的次數越多越好,可以將循環的增量控制定義為寄存器變量,如下所示:
#include <stdio.h>
#include <conio.h>
int main()
{
register int i = 0; // 寄存器變量
double sign = 1.0, res = 0, ad = 1.0;
for(i=1; i<=100000000; i++)
{
res += ad;
sign=-sign;
ad=sign/(2*i+1);
}
res *= 4;
printf("pi is %f", res);
getch();
return 0;
}
運行結果:
pi is 3.141593
關於寄存器變量有以下事項需要注意:
-
為寄存器變量分配寄存器是動態完成的,因此,只有局部變量和形式參數才能定義為寄存器變量。
-
局部靜態變量不能定義為寄存器變量,因為一個變量只能聲明為一種存儲類別。
-
寄存器的長度一般和機器的字長一致,只有較短的類型如 int、char、short 等才適合定義為寄存器變量,諸如 double 等較大的類型,不推薦將其定義為寄存器類型。
-
CPU的寄存器數目有限,即使定義了寄存器變量,編譯器可能並不真正為其分配寄存器,而是將其當做普通的auto變量來對待,為其分配棧內存。當然,有些優秀的編譯器,能自動識別使用頻繁的變量,如循環控制變量等,在有可用的寄存器時,即使沒有使用 register 關鍵字,也自動為其分配寄存器,無須由程序員來指定。