桓臺建設(shè)網(wǎng)站百度網(wǎng)址導(dǎo)航
變量的四大存儲類型static extern auto register
- 外部變量(全局變量)extern----全局靜態(tài)存儲區(qū)
- 定義 引用性聲明
- ?易錯點:函數(shù)之外未定義的變量一般是外部變量 extern
- 全局變量 與 局部變量的區(qū)別
- ?? 謹(jǐn)記:聲明可以多次,定義只能一次
- extern的生存周期
- 靜態(tài)存儲類— static
- 定義
- ?易錯點:static 變量的值只會在第一次初始化時被賦值,之后在函數(shù)調(diào)用之間保持不變。
- static的生存周期— 取決于程序運行周期,不僅僅在函數(shù)調(diào)用期間
- extern與static的作用域和周期的不同
- 作用域:
- 周期:
- auto 自動存儲類別— 默認(rèn) — 動態(tài)存儲
- register—— 寄存器存儲
- 存儲在內(nèi)存中 CPU寄存器
- ??重點:register 的注意事項
- 用于單片機的道理——提高效率
- 總結(jié):四種存儲類別對比
- 在C語言中變量和函數(shù)有數(shù)據(jù)類型和存儲類型兩個屬性,因此變量定義的一般形式為:存儲類型 數(shù)據(jù)類型 變量名表;
C語言提供了一下幾種不同的存儲類型:
(1) 自動變量(auto)
(2) 靜態(tài)變量(static)
(3) 外部變量(extern)
(4) 寄存器變量(register)
下面一個一個介紹:
?
外部變量(全局變量)extern----全局靜態(tài)存儲區(qū)
定義 引用性聲明
標(biāo)準(zhǔn)定義格式:extern 類型名 變量名;
如果在所有函數(shù)之外定義的變量沒有指定其存儲類別,那么它就是一個外部變量
,它的作用域是從它的定義點到本文件的末尾
(在單個源文件中的確是這樣,如果有多個源文件,全局變量的作用范圍不是從變量定義處到該文件結(jié)尾,而是在其他文件中也有效),但是如果要在定義點之前或者其他文件中使用它,那么就需要使用關(guān)鍵字extern對其進(jìn)行聲明(注意不是定義,編譯器并不為其分配內(nèi)存)
- 總結(jié)說也就是,函數(shù)之外沒有指定存儲類別的,一般是外部變量,作用域是定義位置到文件末尾。但是要在其他文件中使用需要extern聲明,不分配內(nèi)存。
舉例說明:
具體實現(xiàn)步驟如下:
在 file1.c 文件中定義全局變量 count:int count = 0;在 file2.c 文件中使用 extern 聲明 count 變量:extern int count;
這樣,在 file2.c 文件中就可以訪問 count 變量了,而不需要重新定義它。在編譯時,兩個源文件將被鏈接起來,共享同一個 count 變量。
?
注意:extern int i 是聲明,但是初始化賦值就是定義了。
extern int i; //是聲明,不是定義,沒有分配內(nèi)存
int i; //是定義
如果在聲明的時候給變量賦值,那么就和去掉extern直接定義變量賦值是等價的
extern int a = 10;//盡量不要寫這種定義方式
int a = 10;//上述兩條語句等價
盡量不要這樣寫,這樣就分配內(nèi)存空間了
?
?易錯點:函數(shù)之外未定義的變量一般是外部變量 extern
也就是說一般沒指定存儲類別的變量咱們常見的是extern定義的,也就是說 全局變量是聲明,局部變量(函數(shù)變量)是定義!
,區(qū)別在于占用空間。
舉例如下:
//說一般沒指定存儲類別的變量咱們常見的是extern定義的,也就是說 **`全局變量是聲明,局部變量(函數(shù)變量)是定義!`** ,區(qū)別在于占用空間。
#include<stdio.h>//全局變量
int a;
int a; //函數(shù)外邊,相當(dāng)于extern聲明?? ,聲明可以??????int main()
{int b;int b; //函數(shù)內(nèi)部,相當(dāng)于局部變量。相當(dāng)于auto int b;定義只能一次。會報錯return 0;
}
?
全局變量 與 局部變量的區(qū)別
Tips:
定義:表示創(chuàng)建變量或分配存儲單元
聲明:說明變量的性質(zhì),但并不分配存儲單元
extern int i; //是聲明,不是定義,沒有分配內(nèi)存
int i; //是定義
如果在聲明的時候給變量賦值,那么就和去掉extern直接定義變量賦值是等價的
extern int a = 10;//盡量不要寫這種定義方式
int a = 10;//上述兩條語句等價
(注意上面的不同語句對聲明和定義的區(qū)分,對源文件中的局部變量來說是成立的(也就是.c文件),而對于源文件中的全局變量(外部變量)int a和在頭文件中的int a就不能用上面的語句來解釋聲明和定義的區(qū)別)
補充:定義和聲明的一個小坑,對于int a;來說,在源文件中,如果是全局變量的話就是聲明,如果是局部變量的話就是定義
?
?? 謹(jǐn)記:聲明可以多次,定義只能一次
?
extern的生存周期
extern 關(guān)鍵字只是聲明一個變量,它并不會直接創(chuàng)建變量的實體,因此沒有生命周期的概念。
具體來說,extern 只是向編譯器聲明某個變量已經(jīng)在別處定義過,讓編譯器在編譯時知道這個變量的存在。這樣,在鏈接階段,編譯器就能夠?qū)⑺杏玫皆撟兞康拇a與它的定義進(jìn)行鏈接,從而使得程序能夠正常運行。
因此,extern 聲明的變量的生命周期與其定義的實體的生命周期是一致的,即它們都是在程序運行期間一直存在的。這意味著,如果一個全局變量在某個文件中被定義為 extern,那么它的生命周期將與整個程序的生命周期相同,只要程序運行,該變量就一直存在。
?
靜態(tài)存儲類— static
定義
- 在 C 語言中,可以使用
static 關(guān)鍵字
來定義靜態(tài)存儲類變量或函數(shù)。
對于靜態(tài)存儲類變量,static 關(guān)鍵字用于改變變量的存儲位置和作用域。具體來說,使用 static 定義的變量存儲在靜態(tài)存儲區(qū)中,具有靜態(tài)持續(xù)性,即使函數(shù)返回,它的值也會保留。而且,static 變量只能在當(dāng)前文件中訪問,不能被其他文件訪問。
- 定義格式:
static 類型符 變量名稱 = 初始值;
舉例:
#include <stdio.h>void func(void);static int count = 10; // 定義靜態(tài)存儲類變量int main()
{while (count--){func();}return 0;}
void func(void)
{static int i = 5; // 定義靜態(tài)存儲類變量i++;printf("i is %d and count is %d\n", i, count);
}
在上面的代碼中,count 和 i 都是靜態(tài)存儲類變量,它們的值在函數(shù)調(diào)用之間保持不變。同時,count 只能在當(dāng)前文件中訪問,而 i 只能在 func() 函數(shù)中訪問。
?
?易錯點:static 變量的值只會在第一次初始化時被賦值,之后在函數(shù)調(diào)用之間保持不變。
舉例說明:
#include <stdio.h>void func(void);int main()
{for (int i = 0; i < 5; i++){func();}return 0;
}void func(void)
{static int i = 5; // 定義靜態(tài)存儲類變量 staticint j = 5; // 定義自動存儲類變量 auto i++; // 對靜態(tài)變量進(jìn)行自增j++; // 對自動變量進(jìn)行自增printf("i is %d and j is %d\n", i, j);
}
在上面的示例中, func() 函數(shù)中定義了一個靜態(tài)變量 i 和一個自動變量 j。在 func() 函數(shù)的每次調(diào)用中,i 的值都會自增1,而j 的值則會在每次調(diào)用中被初始化為5,然后自增1。
運行程序,執(zhí)行效果如下:
i is 6 and j is 6
i is 7 and j is 6
i is 8 and j is 6
i is 9 and j is 6
i is 10 and j is 6
可以看到,static 變量 i 的值在函數(shù)調(diào)用之間保持不變,并且在每次調(diào)用時自增1,而自動變量 j 的值在每次調(diào)用時都會被重新初始化為5,然后自增1。
這說明了 static 變量的值不會改變的原因是因為它們具有靜態(tài)存儲期和靜態(tài)鏈接性。
如果定義為全局靜態(tài)變量也不會變,還是會保持值,不會被初始化。
#include <stdio.h>void func(void);
static int i = 5; // 定義靜態(tài)存儲類變量 static
int main()
{for (int i = 0; i < 5; i++){func();}return 0;
}void func(void)
{int j = 5; // 定義自動存儲類變量 auto i++; // 對靜態(tài)變量進(jìn)行自增j++; // 對自動變量進(jìn)行自增printf("i is %d and j is %d\n", i, j);
}
不會變化的具體原因是:
static 定義的變量值不會變化的具體原因是因為:
- 靜態(tài)存儲期:static 變量被分配在靜態(tài)存儲區(qū)中,該區(qū)域的變量在程序運行期間一直存在,而不是像局部變量一樣在函數(shù)調(diào)用結(jié)束后銷毀。
- 靜態(tài)鏈接性:static 變量具有靜態(tài)鏈接性,只能被定義它的源文件內(nèi)的函數(shù)訪問,其他文件無法訪問。
- 變量只被初始化一次:static 變量只被初始化一次,在其它時刻,只是在其原有的基礎(chǔ)上進(jìn)行修改。因此,變量的值在函數(shù)調(diào)用之間保持不變。
綜上所述,static 變量值不會改變的原因是因為它們具有 靜態(tài)存儲期和靜態(tài)鏈接性,以及變量只被初始化一次
,所以在函數(shù)調(diào)用之間保持不變。
這個易錯點很重要,也是static的特別之處。特點。
?
static的生存周期— 取決于程序運行周期,不僅僅在函數(shù)調(diào)用期間
static 定義的變量有靜態(tài)存儲期,意味著它們在程序運行期間一直存在,直到程序結(jié)束才被銷毀。在函數(shù)內(nèi)部,static 定義的變量只會被初始化一次,在其它時刻,只是在其原有的基礎(chǔ)上進(jìn)行修改,因此其值在函數(shù)調(diào)用之間保持不變。
具體來說,static 定義的變量的生存周期取決于其定義位置
。如果在函數(shù)內(nèi)部定義了 static 變量,則該變量的生存周期為整個程序的運行期間,而不是僅在函數(shù)調(diào)用期間。
舉例:
#include <stdio.h>void test()
{static int i = 0;printf("i = %d\n", i);i++;
}int main()
{for (int j = 0; j < 3; j++){test();}return 0;
}
在這個示例中,test() 函數(shù)內(nèi)部定義了一個 static 變量 i,并在每次調(diào)用 test() 函數(shù)時輸出 i 的值。由于 i 是 static 變量,所以它的值在函數(shù)調(diào)用之間保持不變,并且在每次調(diào)用時自增1。
運行程序如下:
i = 0
i = 1
i = 2
可以看到,i 的值在函數(shù)調(diào)用之間保持不變,每次調(diào)用時自增1,這說明了 static 變量的生存周期是整個程序的運行期間,而不是僅在函數(shù)調(diào)用期間。
?
下面總結(jié)下區(qū)別 : 對比記憶
extern與static的作用域和周期的不同
extern 和 static 是 C 語言中的兩個關(guān)鍵字,它們在變量的作用域和生命周期上有很大的不同。
作用域:
-
extern 關(guān)鍵字用于聲明外部變量,也就是在一個源文件中定義,而在另一個源文件中聲明。外部變量具有全局作用域,也就是說,它們的作用域不限于定義它們的函數(shù)或代碼塊。
-
static 關(guān)鍵字用于定義靜態(tài)變量,靜態(tài)變量的作用域僅限于定義它們的函數(shù)或代碼塊,也就是說,它們只能在其定義的代碼塊中訪問。
周期:
-
extern 關(guān)鍵字用于聲明外部變量,其定義和初始化在另一個源文件中進(jìn)行。因此,外部變量的生命周期和作用域是整個程序的運行期間,也就是說,它們在程序開始時創(chuàng)建,在程序結(jié)束時銷毀。
-
static 關(guān)鍵字用于定義靜態(tài)變量,靜態(tài)變量的生命周期是整個程序的運行期間。在函數(shù)內(nèi)部定義 static 變量,這個變量只會被初始化一次,然后在函數(shù)調(diào)用之間保持不變。
綜上所述,extern 和 static 關(guān)鍵字定義的變量的作用域和生命周期都有所不同,需要根據(jù)具體的需求來選擇使用哪個關(guān)鍵字來定義變量。
因此,你可以這樣理解:
-
extern 關(guān)鍵字聲明的變量可以在整個程序中訪問,它的生命周期和作用域是全局的,直到程序結(jié)束時被銷毀。
-
static 關(guān)鍵字定義的變量只能在同一個源文件中訪問,它的生命周期是整個程序的運行期間,但作用域是局部的,只能在定義它的函數(shù)或代碼塊中訪問,直到程序結(jié)束時被銷毀。
-
還有個不同之處是
extern是聲明,static是定義。
這取決于extern的特殊性
?
auto 自動存儲類別— 默認(rèn) — 動態(tài)存儲
在 C 語言中,auto 存儲類型是默認(rèn)的存儲類型,如果沒有顯式地指定存儲類型,則變量會被默認(rèn)為自動存儲類型。
舉例:在函數(shù)內(nèi)部定義一個自動變量:
void myFunction() {auto int x = 0;// ...
}
自動存儲類型的變量會在定義它的函數(shù)或代碼塊中被創(chuàng)建,當(dāng)函數(shù)或代碼塊執(zhí)行完畢時,自動存儲類型的變量也會被銷毀。這意味著,變量的生命周期僅限于它所在的函數(shù)或代碼塊內(nèi)部,不能被其他函數(shù)或代碼塊訪問。
在上述代碼中,變量 x 被定義為自動存儲類型。它的作用域和生命周期僅限于函數(shù) myFunction() 中,在函數(shù)執(zhí)行完畢后會被銷毀。
-
需要注意的是,在 C 語言中,使用 auto 聲明變量時,不能同時給出變量的存儲類別說明符,例如不能將 auto 與 static 或 extern 同時使用,因為這樣會產(chǎn)生二義性。
-
不賦初值的話,隨機值
-
不專門聲明為 static 存儲類別,都是動態(tài)地分配存儲空間的,數(shù)據(jù)存儲在動態(tài)存儲區(qū)中。
-
關(guān)鍵字 auto 可以省略,auto 不寫則隱含定為“自動存儲類別”,屬于動態(tài)存儲方式
?
register—— 寄存器存儲
register 是 C 語言中的一種存儲類別(Storage Class),它用于告訴編譯器將變量存儲在寄存器中。在 C 語言中,變量的存儲位置可以是寄存器、堆棧或靜態(tài)存儲區(qū),使用 register 存儲類別可以幫助我們優(yōu)化代碼性能,因為寄存器訪問速度比訪問內(nèi)存快。
存儲在內(nèi)存中 CPU寄存器
在 C 語言中,使用 register 關(guān)鍵字聲明變量時,它將被存儲在 CPU 的寄存器中,而不是存儲在內(nèi)存中。由于寄存器是 CPU 內(nèi)部的高速存儲器,所以變量的訪問速度會更快,從而提高程序的執(zhí)行效率。但需要注意的是,變量的存儲位置和寄存器的數(shù)量都是由編譯器決定的,使用 register 關(guān)鍵字只是向編譯器建議將變量存儲在寄存器中,但并不保證一定會存儲在寄存器中。
需要注意的是,由于寄存器的數(shù)量是有限的,所以并不是所有的變量都可以存儲在寄存器中。編譯器會根據(jù)實際情況和編譯器選項來決定哪些變量應(yīng)該存儲在寄存器中。因此,使用 register 關(guān)鍵字并不一定會提高程序的性能,有時可能會適得其反。建議只在必要時使用 register 關(guān)鍵字,或者讓編譯器自動決定變量的存儲位置。
舉例:例如,在函數(shù)內(nèi)部定義一個 register 變量:
void myFunction() {register int x = 0;// ...
}
在上述代碼中,變量 x 被定義為 register 存儲類型,它將被存儲在寄存器中(如果編譯器允許的話)。由于寄存器的訪問速度比內(nèi)存快,因此訪問變量 x 的速度會更快,從而提高程序的性能。
??重點:register 的注意事項
-
用register修飾的變量只能是
局部變量和函數(shù)參數(shù)
,不能是全局變量和靜態(tài)變量。因為cpu的資源有限,因此不可能一直讓一個變量一直占著cpu寄存器 -
register變量一定是cpu可以接受的值
-
不可以用&運算符對register變量進(jìn)行取址。 使用 register 存儲類別的變量不能直接獲取其內(nèi)存地址,因為它們可能存儲在寄存器中,而寄存器沒有地址。 使用 register 存儲類別的變量不要取地址,因為這樣會導(dǎo)致編譯器無法將其存儲在寄存器中,從而影響程序的執(zhí)行效率。
-
register只是請求寄存器變量,不一定會成功。
總之,使用 register 存儲類別應(yīng)該根據(jù)具體的應(yīng)用場景進(jìn)行考慮,避免過度使用 register 存儲類別,從而影響程序的性能。
?
用于單片機的道理——提高效率
register 存儲類別在單片機編程中也經(jīng)常被使用。由于單片機資源有限,使用寄存器存儲變量可以節(jié)約存儲空間,提高程序執(zhí)行效率。特別是在需要頻繁訪問變量的場景下,使用 register 存儲類別可以提高程序的響應(yīng)速度。
舉例來說,如果需要在單片機中使用計時器,可以定義一個 register 變量來存儲計時器的值。這樣,在每次需要使用計時器時,就可以直接訪問寄存器中的值,而不需要從內(nèi)存中讀取,從而提高程序的執(zhí)行效率。
需要注意的是,在單片機編程中使用 register 存儲類別需要謹(jǐn)慎,因為寄存器的數(shù)量很少,而且需要編譯器支持。過度使用 register 存儲類別可能會導(dǎo)致編譯器無法將變量存儲在寄存器中,從而影響程序的性能。因此,需要在具體應(yīng)用場景下根據(jù)實際情況決定是否使用 register 存儲類別。
?
總結(jié):四種存儲類別對比
下表對 C 語言中的四種存儲類別進(jìn)行了比較,包括了生存周期、作用域、存儲方式、存儲區(qū)域等方面:
存儲類別 | 生命周期 | 作用域 | 存儲方式 | 存儲區(qū)域 | 使用場景 |
---|---|---|---|---|---|
auto | 函數(shù)內(nèi)部 | 塊級別 | 棧 | RAM | 用于存儲局部變量 |
static | 整個程序 | 塊級別 | 數(shù)據(jù)段 | RAM | 用于存儲局部變量的持久性版本 |
extern | 整個程序 | 文件級 | 數(shù)據(jù)段 | RAM | 用于在不同的源文件中共享全局變量 |
register | 函數(shù)內(nèi)部 | 塊級別 | 寄存器 | CPU | 用于提高變量的訪問速度 |
其中,“存儲方式”指的是變量在內(nèi)存中的存儲方式,包括棧、堆、數(shù)據(jù)段和寄存器等;“存儲區(qū)域”指的是變量在程序執(zhí)行期間所占用的存儲區(qū)域,包括 RAM 和 CPU 寄存器等。