寫在前面
今天下午一個同事問「register」關鍵字是什么作用?噢,你說的是「register」啊,它的作用是……腦袋突然斷片兒,我擦,啥意思來着,這么熟悉的陌生感。做C語言開發時間也不短了,不過好像沒有用到過「register」,但作用還是知道的,一下子想不起來了,一萬個草泥馬飛奔過來。
其實C語言中除了register
外,還包含常見的const
、static
、volatile
、auto
、extern
等修飾符,現在一起再總結一下好了。
register 修飾符
register,寄存器變量,告訴編譯器它所聲明的變量在程序中使用的頻率非常高,請編譯器盡量將此變量放在寄存器中,這樣程序執行速度更快。但實際上編譯器不一定這么做,可以忽略此選項。
register 修飾符的幾點注意點:
- 變量必須是 CPU 接受的類型,單個值,長度小於等於整數的長度
- 只能使用於局部變量和函數形參,全局(register)變量是非法的
- 無論寄存器變量是否存放在寄存器中,它地址都是不能訪問的(取&)
- 其實過量的寄存器聲明並沒有什么壞處,寄存器可以忽略
const 修飾符
const修飾普通變量
有時候我們希望定義一個變量,它的值在整個作用域都不能變,比如定義緩沖區大小等,可以用 const 來修飾。
// 定義常量 strlen
const int strlen = 4096;
// 試圖修改 strlen 變量,編譯器會報錯
strlen = 2048;
一般常量在定義時同時進行初始化,否則在定義完之后不能對其進行賦值操作,常見的初始化方式有:
const int num = get_num(); // 運行時初始化
const int num = n; // 運行時初始化
const int num = 10; // 編譯時初始化
const 變量真的就不能修改嗎?看個例子:
const int bufsize = 1024;
int *p = &bufsize;
*p = 2048;
printf("bufsize = %d\n", bufsize);
打印結果是2048
。其實 const 修飾的變量不變的本質含義是程序中通過引用變量符號 bufsize 時不能夠進行修改,而不是 bufsize 變量所指向那段內存數據不能修改。
const修改指針變量
const 可以與指針變量一起使用,可以限制指針變量,也可以限制指針變量指向的內容。
const int *ptr; // 指針指向內容不能修改
int const *ptr; // 與第1種等價
int* const ptr; // 指針ptr變量本身不能修改
const int* const ptr; // 指針變量和指針變量指向內容都不能修改
const修改函數參數
其實C語言中使用 const 定義常量並沒有什么優勢,完全可以使用#define
來替代。const 通常用在函數形參中,當形參是一個指針,為了防止函數內部修改指針指向的內容時,就可以用 const 限制。
size_t strlen(const char *s);
int strcmp(const char *s1, const char *s2);
常見C語言標准庫中都有const限制,在我們自定義函數中也可以適當使用 const 來保證程序的健壯性。
const 類型與非 const 類型轉換
當一個指針類似const char *str1
,表示str1
指針指向內容不能修改;但如果將 str1 賦值給 str2,這時 str2 沒有通過 const 限制,通過 str2 就可以修改指針指向內容,這就失去了 const 的意義,編譯器是不提倡這么做的。
const 與非 const 是兩種類型,將非 const 指針賦值給 const 指針,編譯器接受;如果將 const 指針賦值給非 const 指針,這樣將增加指針變量的權限,不安全,有可能發生寫入的危險。所以我們在寫程序時遵守,對指針類型盡可能加 const 修飾;不能將 const 指針賦值給 const 指針。
static 修飾符
static 修飾符在程序中使用最為廣泛,它大概有如下幾種用法:
- 修飾局部變量:增加了局部變量的生命周期,若定義未初始化,則默認初始化為0
- 修飾全局變量:縮小了全局變量的作用域,限制在本模塊(文件)中訪問
- 修飾函數:縮小了函數的作用於,限制函數只能被本模塊調用
volatile 修飾符
關鍵字 volatile 感覺是和 register 有點相反的意思,表示變量隨時可能被修改,且系統對實時性要求很高,請一定從內存中讀取內容,不要直接拷貝寄存器中的數據,有可能數據老舊。常見的使用場合包括中斷服務程序和嵌入式系統的寄存器相關操作。
extern 修飾符
關鍵字 extern 常用在變量和函數聲明前,用來說明此變量或函數是在別處定義過的,要在此處引用。
在 hello.c 中:
void hello()
{
printf("Hello.\n");
}
在 main.c 文件中:
extern void hello();
hello(); // 聲明之后調用 hello 函數
在 main.c 文件被編譯時,告訴編譯器hello()
在別的地方定義過了,這里只是引用一下,放心編譯好了,在程序最后鏈接的時候會去找hello
實際定義的函數。
auto 修飾符
關鍵字 auto 其實可以理解為就是局部變量的顯示說明,程序中很少去顯示聲明某個變量為 auto 的。