變量的作用域
引入問題
我們在函數設計的過程中,經常要考慮對于參數的設計,換句話說,我們需要考慮函數需要幾個參數,需要什么類型的參數,但我們并沒有考慮函數是否需要提供參數,如果說函數可以訪問到已定義的數據,則就不需要提供函數形參。
那么我們到底要不要提供函數形參,取決于什么?答案就是變量的作用域(如果函數在變量的作用域范圍內,則函數可以直接訪問數據,無需提供形參)
變量作用域
**概念:**變量的作用范圍,也就是說變量在什么范圍有效。
變量的分類
根據變量的作用域不同,變量可以分為:
-
全局變量
說明:定義在函數之外,也稱之為外部變量或者全程變量。
作用域:從全局變量定義到本源文件結束。
初始值:整型和浮點型,默認值是0;字符型,默認值是\0;指針型,默認值NULL
舉例:
int num1; // 全局變量,num1能被fun1、fun2、main共同訪問void fun1(){}int num2; // 全局變量,num2能被fun2、main共同訪問void fun2(){}void main(){}int num3; // 全局變量,不能被任何函數訪問
-
局部變量
說明 作用域 初始值 形式參數(形參) 函數作用域 隨機值,需要手動賦初值 函數內定義的變量 函數作用域 隨機值,需要手動賦初值 復合語句中定義的變量 塊作用域 隨機值,需要手動賦初值 for循環表達式1定義的變量 塊作用域 隨機值,需要手動賦初值 舉例:
// a,b就是形式參數(局部變量)int add(int a, int b){return a + b;}int add2(int a, int b){// z就是函數內定義的變量(局部變量)int z = a + b;return z;}int list(int arr[], int len){// i就是for循環表達式1的變量(局部變量)for(int i = 0; i < len; i++){// num就是復合語句中定義的變量(局部變量)int num = arr[i];}}
使用全局變量的優缺點:
優點:
- 利用全局變量可以實現一個函數對外輸出的多個結果數據。
- 利用全局變量可以減少函數形參的個數,從而降低內存消耗,以及因為形參傳遞帶來的時間消耗。
缺點:
- 全局變量在程序的整個運行期間,始終占據內存空間,會引起資源消耗。
- 過多的全局變量會引起程序的混亂,操作程序結果錯誤。
- 降低程序的通用性,特別是當我們進行函數移植時,不僅僅要移植函數,還要考慮全局變量。
- 違反了“高內聚,低耦合”的程序設計原則。
總結:
? 我們發現弊大于利,建議盡量減少對全局變量的使用,函數之間要產生聯系,僅通過實參+形參的方式產生聯系。
作用域舉例
注意:
如果全局變量和局部變量同名,程序執行的時候,就近原則(區分作用域)
int a = 10; // 全局變量 全局作用域int main(){int a = 20; // 局部變量 函數作用域printf("%d\n", a); // 20 就近原則for (int a = 0; a < 5; a++) // 局部變量 塊作用域{printf("%d", a); // 0 1 2 3 4 就近原則}printf("%d\n",a); // 20 就近原則}
變量的生命周期
定義
**概念:**變量在程序運行中的存在時間(內存申請到內存釋放的時間)
根據變量存在的時間不同,變量可分為靜態存儲方式和動態存儲方式
變量的存儲類型
語法:
變量的完整定義格式: [存儲類型] 數據類型 變量列表;
存儲類型:
-
auto
auto存儲類型只能修飾局部變量,被auto修飾的局部變量是存儲在動態存儲區(棧區和堆區)的。auto也是局部變量默認的存儲類型。
int main(){int a;int b;// 以下寫法等價于上面寫法auto int a;auto int b;int a,b;// 以下寫法等價于上面寫法auto int a,b;}
-
static
**修飾局部變量:**局部變量會被存儲在靜態存儲區。局部變量的生命周期被延長。但是作用域不發生改變,不推薦
**修飾全局變量:**全局變量的生命周期不變,但是作用域衰減,一般限制全局變量只能在本源文件內訪問,其他文件不可訪問。
**修飾函數:**被static修飾的函數,只能被當前文件訪問,其他引用該文件的文件是無法訪問的,有點類似于java中private
-
extern
外部存儲類型:只能修飾全局變量,此全局變量可以被其他文件訪問,相當于擴展了全局變量的作用域。
extern修飾外部變量,往往是外部變量進行聲明,聲明該變量是在外部文件中定義的。起到一個標識作用。函數同理。
demo01.c
#include "demo01.h"int fun_a = 10;int fun1(){..}
demo02.c
#include "demo01.h"// 聲明訪問的外部文件的變量extern int fun_a;// 聲明訪問的外部文件的函數extern int fun1();int fun2();
-
register
寄存器存儲類型:只能修飾局部變量,用register修飾的局部變量會直接存儲到CPU的寄存器中,往往將循環變量設置為寄存器存儲類型(提高讀的效率)
for (register int i = 0; i < 10; i++){...}
static關鍵字的作用
- static修飾局部變量,延長其生命周期,但不影響局部變量的作用域。
- static修飾全局變量,不影響全局變量的生命周期,會限制全局變量的作用域僅限本文件內使用(私有化);
- static修飾函數:此函數就稱為內部函數,僅限本文件內調用(私有化)。
static int funa(){..}
內部函數和外部函數
- 內部函數:使用static修飾的函數,稱作內部函數,內部函數只能在當前文件中調用。
- 外部函數:使用extern修飾的函數,稱作外部函數,extern是默認的,可以不寫(區分編譯環境),也就是說本質上我們所寫的函數基本上都是外部函數,建議外部函數在被其他文件調用的時候,在其他文件中聲明的時候,加上extern關鍵字,主要是提高代碼的可讀性。