http://www.jb51.net/article/42348.htm 2013 1. const 變量聲明中帶有關(guān)鍵詞const,意味著不能通過(guò)賦值,增量或減量來(lái)修改該變量的值,這是顯而易見(jiàn)的一點(diǎn)。指針使用const則要稍微復(fù)雜點(diǎn),因?yàn)椴坏貌话炎屩羔槺旧沓蔀閏onst和指針指向的值成為const區(qū)別開(kāi)來(lái)、下面的聲明表示pf指向的值必須是不變的 constfloat *pf;而pf則是可變的,它可以指向另外一個(gè)const或非const值;相反,下面的聲明說(shuō)明pf是不能改變的,而pf所指向的值則是可以改變的: float* const pf; 最后,當(dāng)然可以有既不能改變指針的值也不能改變指針指向的值的值的聲明方式: constfloat * const pf; 需要注意的是,還有第三種放置const關(guān)鍵字的方法: float const * pf; //等價(jià)于constfloat * pf; 總結(jié)就是:一個(gè)位于*左邊任意位置的const使得數(shù)據(jù)成為常量,而一個(gè)位于*右邊的const使得指針本身成為const 還要注意的一點(diǎn)是關(guān)于const在全局?jǐn)?shù)據(jù)中的使用: 使用全局變量被認(rèn)為是一個(gè)冒險(xiǎn)的方法,它使得數(shù)據(jù)在程序的任何部分都可以被錯(cuò)誤地修改,如果數(shù)據(jù)是const,那么這種擔(dān)心就是多余的了不是嘛?因此對(duì)全局?jǐn)?shù)據(jù)使用const是合理的。 然而,在文件之間共享const數(shù)據(jù)要格外小心,有兩個(gè)策略可以使用。一個(gè)是遵循外部變量的慣用規(guī)則,在一個(gè)文件進(jìn)行定義聲明,在其他文件進(jìn)行引用聲明(使用關(guān)鍵字extern)。 /*file1.c------定義一些全局常量*/ const double PI = 3.14159; /*file2.c-----是用在其他文件中定義的全局變量*/ extern const dounle PI; 另外一個(gè)方法是把全局變量放在一個(gè)include文件里,這時(shí)候需要格外注意的是必須使用靜態(tài)外部存儲(chǔ)類(lèi) /*constant.h----定義一些全局常量*/ static const double PI = 3.14159; /*file1.c-----使用其他文件定義的全局變量*/ #include”constant.h”。 /*file2.c-----使用其他文件定義的全局變量*/ #include”constant.h” 如果不使用關(guān)鍵字static,在文件file1.c和file2.c中包含constant.h將導(dǎo)致每個(gè)文件都有同一標(biāo)識(shí)符的定義聲明ANSI標(biāo)準(zhǔn)不支持這樣做(有些編譯器確實(shí)支持)。通過(guò)使用static, 實(shí)際上給了每個(gè)文件一個(gè)獨(dú)立的數(shù)據(jù)拷貝,如果文件想使用該數(shù)據(jù)與另外一個(gè)文件通話,這樣做就不行了,因?yàn)槊總€(gè)文件只能看見(jiàn)他自己的拷貝,然而由于數(shù)據(jù)是不 可變的,這就不是問(wèn)題了。使用頭文件的好處是不必惦記在一個(gè)文件中進(jìn)行定義聲明,在另一個(gè)文件中進(jìn)行引用聲明,缺點(diǎn)在于復(fù)制了數(shù)據(jù),如果常量很大的話,這 就是個(gè)問(wèn)題了。 2. volatile 限定詞volatile告訴編譯器,該變量除了可被程序改變意外還可以被其他代理改變。典型的它用于硬件地址和其他并行運(yùn)行的程序共享的數(shù)據(jù)。例如,一個(gè)地址中可能保存著當(dāng)前的時(shí)鐘信息。不管程序做些什么,該地址會(huì)隨時(shí)間改變。另一種情況是一個(gè)地址用來(lái)接收來(lái)自其他計(jì)算機(jī)的信息; 語(yǔ)法同const: volatile int a;//a是一個(gè)易變的位置 volatile int * pf;//pf指向一個(gè)易變的位置 把volatile作為一個(gè)關(guān)鍵字的原因是它可以方便編譯器優(yōu)化。 假如有如下代碼: va= x; //一些不使用x的代碼 vb= x; 一個(gè)聰明的編譯器可能注意到你兩次使用了x,但是沒(méi)有改變它的值,它將把x臨時(shí)存貯在一個(gè)寄存器中,接著,當(dāng)vb主要x是的時(shí)候,它從寄存器而非初始的內(nèi)存位置得到x的值來(lái)節(jié)省時(shí)間。這個(gè)過(guò)程被稱(chēng)為緩存。通常緩存是一個(gè)好的優(yōu)化方式,但是如果兩個(gè)語(yǔ)句中間的其他代理改變了x的值的話就不是這樣了。如果沒(méi)有規(guī)定volatile關(guān)鍵字,編譯器將無(wú)從得知這種改變是否可能發(fā)生,因此,為了安全起見(jiàn),編譯器不使用緩存。那是在ANSI以前的情形,現(xiàn)在,如果在聲明中沒(méi)有使用volatile關(guān)鍵字,編譯器就可以假定一個(gè)值在使用過(guò)程中沒(méi)有修改,它就可以試著優(yōu)化代碼。總而言之,volatile使得每次讀取數(shù)據(jù)都是直接在內(nèi)存讀取而不是緩存。 你可能會(huì)覺(jué)得奇怪,const和volatile可以同時(shí)使用,但是確實(shí)可以。例如硬件時(shí)鐘一般不能由程序改變,這使得他成為const,但他被程序以外的代理改變,這使得他成為volatile,所以你可以同時(shí)使用它們,順序是不重要的: const volatile time; volatile表明某個(gè)變量的值可能在外部被改變,優(yōu)化器在用到這個(gè)變量時(shí)必須每次都小心地重新讀取這個(gè)變量的值,而不是使用保存在寄存器里的備份。它可以適用于基礎(chǔ)類(lèi) 型如:int,char,long......也適用于C的結(jié)構(gòu)和C++的類(lèi)。當(dāng)對(duì)結(jié)構(gòu)或者類(lèi)對(duì)象使用volatile修飾的時(shí)候,結(jié)構(gòu)或者類(lèi)的所有成員 都會(huì)被視為volatile. 該關(guān)鍵字在多線程環(huán)境下經(jīng)常使用,因?yàn)樵诰帉?xiě)多線程的程序時(shí),同一個(gè)變量可能被多個(gè)線程修改,而程序通過(guò)該變量同步各個(gè)線程。 簡(jiǎn)單示例: 復(fù)制代碼 代碼如下: DWORD __stdcall threadFunc(LPVOID signal) { int* intSignal=reinterdivt_cast(signal); *intSignal=2; while(*intSignal!=1) sleep(1000); return 0; } 該線程啟動(dòng)時(shí)將intSignal 置為2,然后循環(huán)等待直到intSignal 為1 時(shí)退出。顯然intSignal的值必須在外部被改變,否則該線程不會(huì)退出。但是實(shí)際運(yùn)行的時(shí)候該線程卻不會(huì)退出,即使在外部將它的值改為1,看一下對(duì)應(yīng)的偽匯編代碼就明白了: mov ax,signal 對(duì)于C編譯器來(lái)說(shuō),它并不知道這個(gè)值會(huì)被其他線程修改。自然就把它c(diǎn)ache在寄存器里面。C 編譯器是沒(méi)有線程概念的,這時(shí)候就需要用到volatile。volatile 的本意是指:這個(gè)值可能會(huì)在當(dāng)前線程外部被改變。也就是說(shuō),我們要在threadFunc中的intSignal前面加上volatile關(guān)鍵字,這時(shí) 候,編譯器知道該變量的值會(huì)在外部改變,因此每次訪問(wèn)該變量時(shí)會(huì)重新讀取,所作的循環(huán)變?yōu)槿缦旅鎮(zhèn)未a所示: 注意:一個(gè)參數(shù)既可以是const同時(shí)是volatile,是volatile因?yàn)樗赡鼙灰庀氩坏降馗淖儭K莄onst因?yàn)槌绦虿粦?yīng)該試圖去修改它。 3.restrict 關(guān)鍵字restrict通過(guò)允許編譯器優(yōu)化某幾種代碼增強(qiáng)了計(jì)算支持。記住,它只能用于指針,并且表明指針是訪問(wèn)一個(gè)數(shù)據(jù)對(duì)象的唯一且初始的方式。為了清楚為何這樣做,我們需要看一些例子: 復(fù)制代碼 代碼如下: intar[10]; int* restrict restar = (int*)malloc(10*sizeof(int)); int* par = ar; 這里,指針restar是訪問(wèn)malloc分配的內(nèi)存的唯一而且初始的方式,因此聲明為restrict。然而,par指針既不是初始的,也不是訪問(wèn)數(shù)組ar中數(shù)據(jù)的唯一方式,所以不用restrict限定詞?,F(xiàn)在考慮下面這個(gè)更加復(fù)雜的例子,其中n是一個(gè)int 復(fù)制代碼 代碼如下: for(n= 0;n < 10;n++) { par[n]+= 5; restar[n]+= 5; ar[n]*= 2; par[n]+= 3; restar[n]+= 3; } 知道了restar是訪問(wèn)它所指向的數(shù)據(jù)的唯一初始方式,編譯器就可以用具有同樣效果的一條語(yǔ)句來(lái)替代包含restar的兩個(gè)語(yǔ)句 restar[n]+= 8;/*可以替換*/ 然而將兩個(gè)計(jì)算par的語(yǔ)句精簡(jiǎn)為一個(gè)則會(huì)導(dǎo)致錯(cuò)誤因?yàn)樵趐ar兩次訪問(wèn)數(shù)據(jù)之間,ar改變了該數(shù)據(jù)的值。沒(méi)有關(guān)鍵字restrict,編譯器將不得不設(shè)想比較糟糕的那一種形式,而使用之后,編譯器可以放心大膽的尋找計(jì)算的捷徑。可以將關(guān)鍵字作為指針型函數(shù)參量的限定詞使用,這意味著編譯器可以假定在函數(shù)體內(nèi)沒(méi)有其他標(biāo)志符修改指針指向的數(shù)據(jù),因而可以試著優(yōu)化代碼,反之不然。來(lái)看一下C99標(biāo)準(zhǔn)下C庫(kù)中的兩個(gè)函數(shù),他們從一個(gè)位置把字節(jié)復(fù)制到另一個(gè)位置 void*memcpy(void* restrict s1,const void* restrict s2,size_t n); void*memmove(void* s1,const void * s2,size_t); memcpy要求兩個(gè)指針的位置不能重疊,但memmove沒(méi)有這個(gè)要求。把s1,s2聲明為restrict意味著每個(gè)指針都是相應(yīng)數(shù)據(jù)的唯一訪問(wèn)方式,因此他們不能訪問(wèn)同一數(shù)據(jù)塊。這滿足了不能有重疊的要求。 關(guān)鍵字restrict有兩個(gè)讀者:編譯器,它告訴編譯器可以自由地做一些優(yōu)化的假定。另一個(gè)讀者是用戶,他告訴用戶僅使用滿足restrict要求的參數(shù)。一般,編譯器沒(méi)法檢查你是否遵循了這一限制,如果你蔑視它,也就是讓自己冒險(xiǎn)。 |
|
來(lái)自: 心不留意外塵 > 《ccpp for hardware》