關(guān)鍵字:auto, extern有滑, static跃闹,const
函數(shù):malloc(),free()
主要內(nèi)容:變量的作用域(可見的范圍)和生命周期(它存在多長時間)
存儲類別總結(jié)
C語言中毛好,有5種存儲類別(不包括線程)望艺。下面先定義,再慢慢解釋具體的概念肌访。
自動:
無特殊說明或者帶auto說明符聲明的變量找默,自動存儲期,塊作用域吼驶,無鏈接惩激。如果未初始化店煞,其值未定義。
靜態(tài)风钻,外部鏈接:
在所有函數(shù)外部并且 沒有 使用 static 來聲明變量是靜態(tài)的顷蟀,靜態(tài)存儲期,文件作用域骡技,外部鏈接鸣个。只能被編譯器初始化一次。如果未初始化布朦,其值默認為0囤萤。外部鏈接的意思是,其他 .c 文件 可以 通過extern來調(diào)用該文件中定義的此變量是趴。
靜態(tài)阁将,內(nèi)部鏈接:
在所有函數(shù)外部并且 有 使用 static 來聲明變量是靜態(tài)的,靜態(tài)存儲期右遭,文件作用域做盅,內(nèi)部鏈接。只能被編譯器初始化一次窘哈。如果未初始化吹榴,其值默認為0。外部鏈接的意思是滚婉,其他 .c 文件 不可以 通過extern來調(diào)用該文件中定義的此變量图筹。
靜態(tài),無鏈接:
在某塊中被 static 說明符聲明让腹,靜態(tài)存儲期远剩,塊作用域,無鏈接骇窍。只能被編譯器初始化一次瓜晤。如果未初始化,其值默認為0腹纳。
寄存器變量:
未使用過痢掠,暫不介紹。
概念解釋
存儲期
存儲期表示一個變量在內(nèi)存中存在了多長時間嘲恍。用作用域和鏈接去描述標識符(諸如static, const, extern)足画。
作用域和鏈接
作用域和鏈接可以表明,這個變量可以被程序的哪個塊佃牛,哪個函數(shù)淹辞,哪個文件所調(diào)用。
概念詳解
自動:
學(xué)習(xí)編程期間俘侠,我們見到的大多數(shù)變量都是自動變量象缀。它在 塊中(任意一對花括號中間)被定義彬向,并且無static聲明。自動變量只在被定義的塊中有效攻冷。它在程序執(zhí)行到塊中聲明語句時被創(chuàng)建娃胆, 它在函數(shù)進入塊時被分配內(nèi)存,在該塊結(jié)束時被自動釋放等曼。
靜態(tài):
static變量恐怕是很多初學(xué)者的FAQ里烦,其實了解后就會發(fā)現(xiàn)它的定義非常簡單。那就是static的變量禁谦,在程序被載入到程序結(jié)束期間都存在胁黑,當程序結(jié)束時被自動釋放(即使該static變量是在某塊中被初始化,它占用的內(nèi)存不會在塊結(jié)束時被釋放州泊。)
外部丧蘸,內(nèi)部,無鏈接:
這三個概念其實也非常簡單遥皂。當一個變量像我們include stdio.h 一樣力喷,定義在所有函數(shù)外部,那么這個變量就是有鏈接的演训。如果他同時被static聲明弟孟,那么它是內(nèi)部鏈接,內(nèi)部鏈接不能被其他 .c 通過extern訪問样悟,我們可以理解成只在該文件內(nèi)的 “全局變量”拂募。相反,如果沒有static聲明窟她,那么該文件可以被其他 .c文件通過extern訪問陈症,它是所有文件都能用的“全局變量”。而靜態(tài)無鏈接變量震糖,這種儲存類型的文件只能在被定義的塊中被訪問录肯,換句話說,此變量的作用域是定義它的塊试伙。
存儲類別異同和注意事項
自動變量在被初始化時嘁信,它的值會繼承這塊內(nèi)存上一次儲存的值(如果存在)于样,總之你別期待著它自動變成 0 疏叨。但是如果我們定義 static 變量時,沒有給它初始化穿剖,那么它的值默認為 0 蚤蔓。如果變量是數(shù)組名,那么所有數(shù)組成員都為0 糊余。
一般我們在創(chuàng)建自動變量時秀又,不需要加上 auto单寂,一般加auto是強調(diào),我需要創(chuàng)建一個和 static 變量同名的變量名吐辙,來特指宣决。個人建議沒事閑的別建同名的變量。
塊作用域static變量舉例昏苏,比如for 循環(huán)中的 static 聲明尊沸,例如:
for (int i = 0; i < 2; i++) {
int x = 1;
static int y = 1;
printf("x is %d, y is %d", ++x, ++y);
}
可以設(shè)想一下,這個函數(shù)運行的時候printf都會輸出什么贤惯,
答案是洼专,x是自動變量,自動變量在塊結(jié)束時都會被釋放孵构。
每次進入for循環(huán)塊屁商,x都會被重新聲明為1,所以printf的第一項永遠為2颈墅,
但是static int y = 1這條語句在程序開始時就被分配內(nèi)存蜡镶。
static int y = 1; 這條聲明實際上不是for的一部分。
如果開啟調(diào)試模式恤筛,你會發(fā)現(xiàn)帽哑,程序似乎 跳過 了這條聲明。
因為靜態(tài)變量和外部變量在程序被載入時內(nèi)存已執(zhí)行完畢叹俏。
把這條語句放在for里面是為了告訴編輯器妻枕, 只有這個for循環(huán) 才能 “看到” 該變量。
**這條聲明未在運行時執(zhí)行粘驰。**
-
什么時候需要用到extern屡谐?
如果一個變量定義在另一個文件內(nèi),而我們要在自己的文件內(nèi)使用它蝌数,那么必須使用 extern 來聲明愕掏,我使用了其他文件中的 靜態(tài)外部鏈接 變量。
聲明了外部變量顶伞,可以在函數(shù)中使用extern來強調(diào)饵撑,我接下來要用的變量是外部變量。
int num;
int arr[10];
int main(void){
extern int num;
extern int arr[];
}
這里其實完全可以省略 extern int num; extern int arr[]; 這兩條代碼唆貌,
它們僅僅是為了強調(diào)這兩個變量是全局變量滑潘。
但是要刪除的話就全刪除掉, 如果只刪除了 extern锨咙,那么實際上你是建立了兩個同名的自動變量语卤。
我們正常創(chuàng)建的函數(shù),都是外部定義的,所以這些函數(shù)都可以被其他 .c中使用 extern調(diào)用粹舵。同時函數(shù)也可以被靜態(tài)定義钮孵,靜態(tài)函數(shù)只能在該文件內(nèi)使用。
外部變量只能初始化一次眼滤,且必須在定義變量時進行巴席,并且初始化只能使用常量表達式。
malloc和free
malloc函數(shù)接收一個參數(shù)诅需,那就是大小情妖,返回一個參數(shù),代表生成內(nèi)存的首字節(jié)地址诱担。把這個地址賦值給一個指針變量毡证,指針就可以訪問這個內(nèi)存了。
malloc生成的內(nèi)存是void類型的蔫仙,所以在使用的時候盡量使用強制類型轉(zhuǎn)換料睛。
int *res = (int *)malloc(sizeof(int) * ASize); // (刷過leetcode懂得都懂。摇邦。恤煞。)
free函數(shù)的參數(shù),是之前malloc返回的地址施籍。既然是地址居扒,我們就沒必要非得free當時malloc賦值給的那個指針,只要你free參數(shù)的指針丑慎,指向的是那個地址就行喜喂。
老生常談的問題,為什么一定要free竿裂。假設(shè)你在for循環(huán)中malloc并指向一個自動變量指針玉吁,如果沒有在for循環(huán)中free,那么這個自動變量在每次塊結(jié)束的時候就被釋放腻异,相當于你就再也找不到這個地址了进副,想釋放都釋放不了。那么循環(huán)1萬次悔常,每次都無法釋放影斑,讓冗余的數(shù)據(jù)占據(jù)內(nèi)存,之后很可能就堆棧溢出了机打。這類問題就叫內(nèi)存泄漏(Memory Leak)矫户。
存儲類別和動態(tài)內(nèi)存分配
這個部分詳細說的話,可以再寫一篇博客了姐帚。所以這里只說結(jié)論吏垮。
Malloc生成的變量障涯,存放在堆罐旗。
自動變量膳汪,都存放在棧上。
堆和棧加起來九秀,叫動態(tài)存儲區(qū)遗嗽,這個地方的內(nèi)存一會變大一會變小,所以是動態(tài)的鼓蜒。
靜態(tài) 痹换、常量、全局變量都弹,存儲在靜態(tài)存儲區(qū)娇豫,也叫全局存儲區(qū)。
堆和棧的五大區(qū)別:
(我在百度上一搜畅厢,反而是一個廣告網(wǎng)站里總結(jié)的挺好冯痢,就不附上鏈接了怕廣告嚇到你。框杜。浦楣。)
申請方式不同。棧由系統(tǒng)自動分配咪辱,堆是程序員認為申請開辟振劳。
申請大小不容。棧獲得的空間小油狂,堆獲得的空間大历恐。
申請效率不同。 棧由系統(tǒng)自動分配专筷,速度快夹供,堆速度較慢。
存儲內(nèi)容不同仁堪。 棧在函數(shù)調(diào)用時哮洽,函數(shù)調(diào)用語句的下一條可執(zhí)行語句的地址第一個進棧,然后函數(shù)的各個參數(shù)進棧弦聂,靜態(tài)變量不入棧鸟辅。堆一般在頭部用一個字節(jié)存放堆的大小,堆中內(nèi)容人為安排莺葫。
底層不同匪凉。 棧是連續(xù)的空間,堆是不連續(xù)的空間捺檬。
const類型限定符使用
類型限定符一共有三種: const再层, volatile,restrict. 由于 const 最常用并且我還沒有用到過其他兩個限定符,就先專門總結(jié)一下const吧聂受。
const 的目的是讓被定義的變量無法被改變蒿秦,聽起來挺簡單的,不過很多使用的時候也會有一些小坑蛋济。
const聲明指針時候的位置
const int *p;
int * const p;
const int * const p;
最上面兩條代碼是有區(qū)別的棍鳖。關(guān)注點在 const 是在 * 左邊還是右邊。
第一條代碼聲明了一個 int 型指針變量碗旅,p解引用(*p的值)是不能被改變的渡处。但是,p可以指向其他的值祟辟。
第二條代碼聲明了一個固定地址的p医瘫,即該指針只能指向這個地址,但是這個地址上面的值是可以被改變的旧困。
第三條代碼當然是全部都固定住不能改變醇份。
const in *p = NULL;
*p = 5; // 這里會報錯: Read-only variable is not assignable
雖然解引用(*p的值)是不能被改變的。但是叮喳,p可以指向其他的變量被芳,解引用獲得其他的值。
int main(void)
{
int a = 5;
const int *p = NULL;
p = &a;
printf("%d", *p);
}
對全局數(shù)據(jù)使用const
外部鏈接的全局變量可能被其他文件 extern調(diào)用馍悟,所以為了防止他們被意外更改畔濒,還是加上const較好。
同時如果為了省事锣咒,我們可以把 const double PI = 3.14
這種語句寫在頭文件里面侵状,然后在其他 .c 文件中 extern就好了。
但是這里也有坑毅整,那就是寫在 頭文件 中的全局變量聲明趣兄,最好加上 static ,因為如果不加悼嫉,那么每個 include 了這個 頭文件的 .c文件艇潭,都會多出一句這個代碼 : const double PI = 3.14
而且這種聲明都是外部鏈接啊,那到時候萬一有的文件沒有 include 這個 頭文件戏蔑,而是選擇 extern 這個變量蹋凝, 那么編譯器怎么知道它到底想要的是哪個 PI?
其實C標準是不允許這么做的总棵,所以寫在頭文件里面的東西鳍寂,最好還是加上 static,要不然容易出問題情龄。