C語言中關鍵字auto、static、register、extern、volatile、restrict的作用

auto

(1)auto關鍵字在C語言中只有一個作用,那就是修飾局部變量。
(2)auto修飾局部變量,表示這個局部變量時自動局部變量,自動局部變量分配在棧上。(既然是分配在棧上,說明他如果不初始化的話那麼值就是隨機的…)
(3)平時定義局部變量時就是定義的auto的,只是省略了auto關鍵字而已。可見,auto的局部變量其實就是默認定義的普通的局部變量。

static

(1)static關鍵字在C語言中有2種用法,而且這兩種用法彼此沒有關聯、完全獨立的。其實當年本應該多發明一個關鍵字,但是C語言的作者覺得關鍵字太多不好管理,於是給static增加了一種用法,導致static一個關鍵字居然有兩種截然不同的含義。
(2)static的第一種用法是:用來修飾局部變量,形成靜態局部變量。靜態局部變量和 非靜態區別變量的區別。本質區別是存儲類不同(存儲類不同就衍生出很多不同):非靜態局部變量存儲在棧上(auto),而靜態局部變量分配在數據段或者是bss段。如果顯示初始化爲0則在數據段,如果沒有顯示初始化爲0則存在BSS段。
(3)static的第二種用法是:用來修飾全局變量,形成靜態全局變量。靜態全局變量和非靜態全局變量的區別。區別是在連接屬性不同。

分析:
(1)靜態局部變量在存儲類方面和全局變量一樣。
(2)靜態局部變量在生命週期方面和全局變量一樣。
(3)靜態局部變量和全局變量的區別是:作用域、生命週期。靜態局部變量作用域是代碼塊作用域(和普通局部變量是一樣的)、連接屬性是無連接;全局變量作用域是文件作用域(和函數的作用域是一樣的)、連接屬性方面是外連接。

register

(1)register關鍵字不常用,也只有一個作用,那就是:用register修飾的變量編譯器會盡量把它分配子寄存器中。(平時分配的一般變量都是在內存中)。分配在寄存器中用法是一樣的,但是讀寫效率會高很多。所以說register修飾的變量用在那種變量被反覆高頻率的使用,通過改善這個變量的訪問詳聊可以極大的提升程序運行效率。所以說regis是一種極致提升程序運行效率的手段。
(2)在uboot中用到了一個register類型的變量,gd這個變量是用來存uboot的全局變量的(gd就是global data)。因爲這個全局變量在整個uboot中到處都被訪問,所以定義成register的。
(3)平常寫代碼要被定義成register這種情況很少,一般慎用。
(4)register編譯器只能承諾儘量將register修飾的變量放在寄存器中,但是不保證一定放在寄存器中。主要原因是因爲寄存器數量有限,不一定有用。

extern

(1)extern主要用來聲明全局變量,聲明的目的主要是在a.c中定義全局變量而在b.c中使用該變量。
(2)C語言中沖虛的編譯時以單個.C文件爲單位的,因此在編譯a.c時只考慮a.c中的內容(不會考慮b.c的內容),這就是導致a.c中使用了b.c中定義的變量時在編譯時報錯。解決方案是聲明。
(3)應該在a.c中使用此變量之前先聲明,聲明就是告訴a.c我在別的文件中定義了該變量,並且它的原型和聲明的一樣,將來在連接的時候連接器會在別的.o文件中找到這個同名變量。此變量在a.c中聲明的時候不能夠加初始化。

volatile

(1)volatile字面意思就是可變的、易變的。C語言中volatile用來修飾一個變量,表示這個變量,表示這個變量可以被編譯器之外的東西改變。編譯器之內的意思是變量值的改變是代碼的作用,編譯器之外的改變不是代碼造成的,或者不是當前代碼造成的,編譯器在編譯當前代碼時無法預知。譬如說在中斷處理程序isr中更改了這個變量的值,譬如說多線程中別的線程更改了這個變量的值,譬如說硬件自動更改了這個變量的值(一般這個變量是一個寄存器的值)。

(2)以上說的三種情況(中斷isr中引用的變量,多線程中公用的變量,硬件會更改的變量)都是我們編譯器在編譯時無法預知的更改,此時我們應該使用volatile告訴編譯器,這個變量屬於這種情況–可變的、易變的。編譯器在遇到volatile修飾的變量時就不會丟該變量的訪問進行優化,就不會出現錯誤。

(3)編譯器的優化在一般情況下非常好,可以幫助提升程序效率。但是特殊情況(volatile)除外。比如說a=3;b=a;c=b;在無優化的情況下內存要讀三次、寫三次;如果有優化的話:內存讀一次,寫三次就可以了優化成a=b=c=3。如果在執行a=3之後程序中斷來臨,此時將a=10;那麼中斷結束後:b的值爲10(編譯器未優化),b=3(編譯器優化)。此時優化會帶來錯誤,並且這種錯誤很難被發現。如果定義變量時變量會被中斷等改變,則需要聲明成volatile。

(4)volatile是程序猿意識到需要volatile然後在定義變量時加上了volatile。如果你遇到了應該加volatile的情況而沒有加程序可能會被錯誤的優化。如果不應該加volatile而加了的情況程序不會出錯,知識降低了效率。所以我們對於volatile的態度應該是:正確區分,該加的時候加,不加的時候不加,如果不能確定該不該加,爲了保險起見就加上。

restrict

(1)在C99中才支持的,所以很多延續了c89的編譯器是不支持restrict。gcc支持。
(2)restrict也是和編譯器的行爲特徵有關的。
(3)只用來修飾指針,不能修飾普通變量。概括的說,關鍵字restrict只用於限定指針;該關鍵字用於告知編譯器,所有修改該指針所指向內容的操作全部都是基於(base on)該指針的,即不存在其它進行修改操作的途徑;這樣的後果是幫助編譯器進行更好的代碼優化,生成更有效率的彙編代碼。

舉個例子:

int fuc1(int *x,int *y)
{
	*x = 0;
	*y = 1;
	return *x;
}

很顯然函數fuc1()的返回值是0,除非參數x和y的值相同。可以想象,99%的情況下該函數都會返回0而不是1。然而編譯起必須保證生成100%正確的代碼,因此,編譯器不能將原有代碼替換成下面的更優版本.

int fuc1(int* x, int* y)
{
*x = 0;
*y = 1;
return 0;
}

如果加上了restrict關鍵字的話:

int fuc1(restrict int *x,restrict int *y)
{
	*x = 0;
	*y = 1;
	return *x;
}

此時,由於指針 x 是修改 *x的唯一途徑,編譯起可以確認 “*y=1; ”這行代碼不會修改 *x的內容,因此可以安全的優化爲

int fuc1(restrict int* x,restrict  int* y)
{
	*x = 0;
	*y = 1;
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章