(C語言內存二十一)C語言變量的存儲類別和生存期


前言

我們知道,變量是有數據類型的,用以說明它占用多大的內存空間,可以進行什么樣的操作。

除了數據類型,變量還有一個屬性,稱為“存儲類別”。存儲類別就是變量在內存中的存放區域。在進程的地址空間中, 常量區、全局數據區和棧區可以用來存放變量的值

常量區和全局數據區的內存在程序啟動時就已經由操作系統分配好,占用的空間固定,程序運行期間不再改變,程序運行結束后才由操作系統釋放;它可以存放 全局變量、靜態變量、一般常量和字符串常量

棧區的內存在程序運行期間由系統根據需要來分配(使用到變量才分配內存;如果定義了變量但沒有執行到該代碼,也不會分配內存),占用的空間實時改變,使用完畢后立即釋放,不必等到程序運行結束;它可以存放局部變量、函數參數等。

我們可以通過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 的。

來看一個計算 π 的近似值的例子,求解的一個近似公式如下:
image
為了提高精度,循環的次數越多越好,可以將循環的增量控制定義為寄存器變量,如下所示:

#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

關於寄存器變量有以下事項需要注意:

  1. 為寄存器變量分配寄存器是動態完成的,因此,只有局部變量和形式參數才能定義為寄存器變量。

  2. 局部靜態變量不能定義為寄存器變量,因為一個變量只能聲明為一種存儲類別。

  3. 寄存器的長度一般和機器的字長一致,只有較短的類型如 int、char、short 等才適合定義為寄存器變量,諸如 double 等較大的類型,不推薦將其定義為寄存器類型。

  4. CPU的寄存器數目有限,即使定義了寄存器變量,編譯器可能並不真正為其分配寄存器,而是將其當做普通的auto變量來對待,為其分配棧內存。當然,有些優秀的編譯器,能自動識別使用頻繁的變量,如循環控制變量等,在有可用的寄存器時,即使沒有使用 register 關鍵字,也自動為其分配寄存器,無須由程序員來指定。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM