static extern const volatile

static extern const volatile

C語言中的對於變量的定義方式有很多種,使用的地方也有所不同,這邊就對幾種類型進行一個總結,對於實際編程的使用或面試時都會有很大的幫助。

1. static:靜態變量

a、當我們把一個全局變量聲明爲static時:只有它的作用範圍變爲本源文件,也就是屬性由external變爲internal,其它不變;

b、當我們把函數聲明爲static時:它的作用範圍變爲本源文件,也就是屬性由external變爲internal;

c、當我們把局部變量聲明爲static時:默認初始化值爲0,並且只在第一次定義時初始化;內存存儲區域不再是棧,而是在靜態存儲區;生命週期不再是所在函數,而是整個進程;其它不變。

2.extern:引用變量

引用變量一般也需要經常使用,因爲在C語言裏面,全局變量和函數都是默認extern的屬性,當其他文件想使用另一個文件中的變量是,就需要使用extern進行申明。

3.const:只讀變量

只讀變量也稱常量,由const聲明的變量,必須在定義時進行初始化。如下:

const int num = 10; 

在定義處初始化,並且變量的值不允許再改變,既然變量的值都不允許改變,那麼這個變量定義了有啥用?

首先在我們定義數組的時候,數組的大小就可以用const定義的常量來表示,這個就跟#define一樣,但是它是類型安全的,#define是預處理命令,只是進行簡單的字符替換,而編譯器會對const定義的變量進行類型檢查。

其次,當我們需要一個不再改變的變量時,就可以用const,比如說定義一個人的性別,自打你一出生就已經決定了你的性別,不出意外的話,這輩子都不會改變了,所以就把它定義爲只讀的。

當然有人也認爲不定義爲const也可以的嘛,只要自己不改變它就行,但是如果是那樣的話,就需要人爲來控制了,萬一哪天忘了,把它改了怎麼辦?所以對於一些只讀或者常量最好用const來定義。

當我們把const與指針變量放在一起的時候,問題就變得複雜了。比如我們定義如下:

const int *p1;
int const *p2;
int * const p3;
int const * const p4;
  • 指針變量p1:const在數據類型之前,修飾的是p1所指向的對象,所以p1所指向的對象的值爲常量只讀,不能改變,但是p1本身可以改變;

  • 指針變量p2:const在*之前,這種情況與p1相同;

  • 指針變量p3:const在*之後,修飾的是變量p3,所以變量p3本身爲常量只讀,而p3所指向的對象可以改變;

  • 指針變量p4:有兩個const分別修飾變量p4和p4所指向的對象,所以p4本身和p4所指向的對象都爲常量只讀,都不可以改變。

其實這些也很容易記住,只要看const是在*前面還是*後面,在*前修飾的就是指針所指向的對象,在*後修飾的就是指針本身。

下面來舉個簡單的例子說明:

int main(){
    int num1 = 0;
    int num2 = 1;
    int num3 = 2;
    int num4 = 3;
    const int *p1;
    int const *p2;
    //int * const p3; //error(1)
    int * const p3 = &num3;
    //int const * const p4; //error(2)
    int const * const p4 = &num4;
    p1 = &num1;
    //*p1 = 100;//error(3)
    num1 = 100;//此時*p1 = 1;
    //p3 = p4;//error(4)
    *p3 = 100;
    //p4 = p3;//error(5) 
  
    return 0;
}   

在上面代碼中,error(1)和error(2)很容易理解,因爲const在*之後,所以指針p3,p4本身爲只讀,在定義時必須初始化。

error(3)是因爲對於p1指針,const在*之前,所以p1所指向的對象不能改變。

error(4)和error(5)是因爲對於p3,p4,有const在*之後,所以指針本身只讀,在初始化之後,就無法再改變了。

4.volatile:易變變量

volatile提醒編譯器它後面所定義的變量隨時都有可能改變,因此編譯後的程序每次需要存儲或讀取這個變量的時候,都會直接從變量地址中讀取數據。

如果沒有volatile關鍵字,則編譯器可能優化讀取和存儲,可能暫時使用寄存器中的值,如果這個變量由別的程序更新了的話,將出現不一致的現象。一般用在以下幾個地方:

a、並行設備的硬件寄存器(如:狀態寄存器)

b、一箇中斷服務子程序中會訪問到的非自動變量(Non-automatic variables)

c、多線程應用中被幾個任務共享的變量

下面舉例說明。在DSP開發中,經常需要等待某個事件的觸發,所以經常會寫出這樣的程序:

short flag;
void test()
{
do1();
while(flag==0);
do2();
}

這段程序等待內存變量flag的值變爲1(懷疑此處是0,有點疑問,)之後才運行do2()。變量flag的值由別的程序更改,這個程序可能是某個硬件中斷服務程序。

例如:如果某個按鈕按下的話,就會對DSP產生中斷,在按鍵中斷程序中修改flag爲1,這樣上面的程序就能夠得以繼續運行。但是,編譯器並不知道flag的值會被別的程序修改,因此在它進行優化的時候,可能會把flag的值先讀入某個寄存器,然後等待那個寄存器變爲1。

如果不幸進行了這樣的優化,那麼while循環就變成了死循環,因爲寄存器的內容不可能被中斷服務程序修改。爲了讓程序每次都讀取真正flag變量的值,就需要定義爲如下形式:
volatile short flag;

需要注意的是,沒有volatile也可能能正常運行,但是可能修改了編譯器的優化級別之後就又不能正常運行了。因此經常會出現debug版本正常,但是release版本卻不能正常的問題。所以爲了安全起見,只要是等待別的程序修改某個變量的話,就加上volatile關鍵字。

5.指針變量

在介紹const時,舉出了很多有關指針常量的定義,這邊在舉一些有關指針變量的定義。

a) int a;表示一個內存空間,這個空間用來存放一個整數(int);

b) int* a;表示一個內存空間,這個空間用來存放一個指針,這個指針指向一個存放整數的空間,即a)中提到的空間;

c) int** a;表示一個內存空間,這個空間用來存放一個指針,這個指針指向一個存放指針的空間,並且指向的這個空間中的指針,指向一個整數。也簡單的說,指向了一個b)中提到的空間;

d) int (*a)[10];表示一個內存空間,這個空間用來存放一個指針,這個指針指向一個長度爲10、類型爲int的數組;和int**a的區別在於,++、+=1之後的結果不一樣,其他用法基本相同。以上四種類型見上圖表示。

e) int (*a)(int);表示一個內存空間,這個空間用來存放一個指針,這個指針指向一個函數,這個函數有一個類型爲int的參數,並且函數的返回類型也是int。

static extern const volatile的分析就到這邊,有感悟時會持續會更新。

注:以上內容都是本人在學習過程積累的一些心得,難免會有參考到其他文章的一些知識,如有侵權,請及時通知我,我將及時刪除或標註內容出處,如有錯誤之處也請指出,進行探討學習。文章只是起一個引導作用,詳細的數據解析內容還請查看C語言相關教程,感謝您的查閱。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章