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語言相關教程,感謝您的查閱。