C語言中的作用域、鏈接屬性與存儲屬性


C語言中的作用域、鏈接屬性與存儲屬性

一、作用域(scope)

  1. 代碼塊作用域

    表示{}之間的區域,下例所示,a可以在不同的代碼塊里面定義。

    #include<stdio.h>
    
    int main()
    {
        int f(int g){
            return g;
        }
    
        int a = 0;
        {
            int a = 2;
            printf("inner: %d\n", a);
        }
        printf("outter: %d\n", a);
        printf("inline function:%d\n", f(10));
    }
    
    $ ./a.out
    inner: 2
    outter: 0
    inline function:10
    

C語言居然支持了內部函數,好神奇。

```c
//error: ‘i’ redeclared as different kind of symbol
int fun(int i)
{
    int i = 0;
    return i;
}
```
ANSI C中,形參的作用域為函數最外層的那個作用域,不能在函數體內聲明同名的變量(K&R C可以,屏蔽形參)。
  1. 文件作用域

    任何在所有代碼塊之外聲明的標識符都具有文件作用域(file scope)。並且,通過#include指令包含到其他文件中的聲明就好像它們是直接寫在那些文件中一樣。它們的作用域並不局限於頭文件的文件尾。

  2. 原型作用域

    int fun(int a);
    

    原型作用域(prototype scope)只適用於在函數原型中聲明的參數名,將它獨立出來只是為了這個名字不能與其他作用域的符號混淆,它其實沒有什么用得,甚至都可以不寫。事實上,唯一可能出現的沖突就是在同一個原型中不止一次使用同一個名字。

  3. 函數作用域

    它只適用於語句標簽,用於goto語句。《C和指針》的作者說願你永遠不需要這方面的知識,哈哈!!!

二、鏈接屬性

當組成一個程序的各個源文件分別被編譯之后,所有的目標文件以及那些從一個或多個函數庫中引用的函數鏈接在一起,形成可執行文件。問題是,如果相同的標識符出現在幾個不同的源文件中時該怎么辦?標識符的鏈接屬性決定如何處理在不同文件中出現的標識符。標識符的作用域與它的鏈接屬性有關,但這兩者並不相同。

鏈接屬性一共有3種:

  1. none(無)

    總是被當做單獨的個體,也就是說該標識符的多個聲明被當作獨立不同的實體。

  2. internal(內部)

    在同一個源文件中的所有聲明中都指向同一個實體,但位於不同源文件的多個聲明則分屬不同的實體。

  3. external(外部):

    不論聲明多少次、位於幾個源文件都表示同一個實體。

    extern和static兩個關鍵字可以用於設定標識符的鏈接屬性。當沒有這兩個關鍵字時,默認的鏈接屬性與標識符的作用域相關。

    typedef char *a;
    int b;
    int c(int d)
    {
        int e;
        int f(int g);
        ...
    }
    

    上面的代碼塊中,b、c、f有external鏈接屬性,f在本代碼中被調用,定義在其他源文件或者庫中,所以也是external屬性。其他的標識符都是none屬性。

    static關鍵字可以把一個默認為external屬性的標識符改為internal,如上例中,可以把b、c的鏈接屬性改為internal,使其在其他源文件中不可見。

    static int b;
    static int c(int d);
    

    extern 關鍵中可以把none屬性改為external屬性。

    // linkage_test1.c
    #include<stdio.h>
    
    extern int a; // 可選,因為默認就是external
                  // 但是應該寫上,增加程序可讀性
    
    int main() {
        printf("a = %d\n", a);
        extern int b; // 必需,默認為none
        printf("b = %d\n", b);
    }
    
    // linkage_test1.c
    int a = 1;
    int b = 2;
    
    

    最后,當extern關鍵字用於源文件中一個標識符的一次聲明時,它指定該標識符具有external鏈接屬性,但是,如果它用於該標識符的第2次或者以后的聲明時,它並不會更改由第一次聲明所指定的鏈接屬性。如下例所示:

    static int i;
    int func()
    {
        extern int i; //i的鏈接屬性仍然為static
    }
    

三、存儲類型

變量的存儲類型是指存儲變量值的內存類型。變量的存儲類型決定變量何時創建、何時銷毀以及它的值將保持多久。有三個地方可以用於存儲變量:普通內存、運行時堆棧、硬件寄存器。在這三個地方存儲的變量具有不同的特性。

  1. 普通內存變量

    變量的缺省存儲類型取決於它聲明的位置(作用域),凡是在任何代碼塊之外聲明的變量總是存儲與靜態內存中,也就是不屬於堆棧的內存,這類變量成為靜態變量(static),可以通過static關鍵字將一個代碼塊內部變量由堆棧類型變為靜態類型。靜態類型的標識符存在ELF文件的.data(已初始化)或者.bss段(未初始化,默認值為0)。這些變量在程序未運行之前(通過內存加載)已經存在。

  2. 堆棧變量

    在代碼塊內部聲明的變量的默認存儲類型是自動的(automatic),可以使用關鍵字auto指定,但它極少使用,因為完全沒必要。這些自動變量存在堆棧中。

  3. 硬件寄存器變量

    你可以通過關鍵字register來指定,提示程序運行時用硬件寄存器來存儲該變量,但是編譯器可以不鳥你,因為它認為它比你更清楚那個變量該用寄存器那個該用堆棧。

變量的初始化

靜態變量只能用常數進行初始化(其他靜態變量都不行),如果未顯性初始化,則默認值為0;

堆棧變量可以通過任何合法表達式初始化,因為它是在運行時創建,如果不初始化,其默認值為垃圾。

引用

--C和指針/ (美)里科(Reek, K. A.)著;徐波譯. —北京:人民郵電出版社,2008. 4(2016. 5重印)


免責聲明!

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



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