您好,登錄后才能下訂單哦!
本篇內容介紹了“怎么區分作用域,存儲器,鏈接屬性”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
作用域
C語言中,作用域用來描述標識符能夠被哪些區域訪問。
而常見作用域有以下幾種:
塊作用域,可見范圍是從定義處到包含該定義的塊結尾
函數作用域,goto語句的標簽就具有函數作用域
文件作用域,從定義處到定義該文件的末尾都可見。定義在函數之外的變量,就具有文件作用域了。
函數原型作用域,從形參定義處到原型聲明結束
為了便于說明,我們來看一個例子,就很容易理解了:
/**************************** 作者:守望先生 來源:公眾號編程珠璣 個人博客:https://www.yanbinghu.com ***************************************/ #include <stdio.h> int num1 = 222; //定位在函數外,具有文件作用域 static int num2 = 111; //定義在函數外,具有文件作用域 int swap(int *a,int *b); //這里的a,b是函數原型作用域 int swap(int *a,int *b) { if(NULL== a || NULL == b) goto error; else { int temp = *a; //定義在函數內,塊作用域 *a = *b; *b = temp; return 0; } //printf("temp is %d\n",temp); //因為temp具有塊作用域,因此在這里不能直接使用 error://goto語句的標簽,函數作用域,因此在前面就可以引用 { printf("input para is NULL\n"); return -1; } } int main(void) { printf("num1=%d,num2=%d\n",num1,num2); swap(&num1,&num2); //num1 num2具有文件作用域,可以在main函數中直接使用 printf("num1=%d,num2=%d",num1,num2); return 0; }
可以看到,error標簽具有函數作用域,整個函數內都可見,而temp具有塊作用域,因此在大括號外部,不能直接使用它。而num1和num2具有文件作用域,因此main函數可以直接使用它。
鏈接屬性
在《hello程序是如何變成可執行文件的》我們說到了編譯的過程,最后一個步驟就是鏈接。鏈接屬性決定了在不同作用域的同名標識符能否綁定到同一個對象或者函數。或者說,不同作用域的標識符在編譯后是否是同一個實體。
c變量有三種鏈接屬性:
外部鏈接,extern修飾的,或者沒有static修飾的具有文件作用域的變量具有外部鏈接屬性
內部鏈接,static修飾的具有文件作用域的變量具有內部鏈接屬性
無鏈接,塊作用域,函數作用域和函數原型作用域的變量無鏈接屬性
再稍作解釋,沒有static修飾,且具有文件作用域的變量,他們在鏈接時,多個同名標識符的變量最終都綁定到同一個實體。而static修飾的具有文件作用域的變量就不一樣了,不同文件內,即便標識符名字相同,它們也綁定到了不同的實體。
因此,如果我們希望某個變量或函數只在某一個文件使用,那么使用static修飾是一個很好的做法。
同樣的,來看一個例子。
/**************************** 作者:守望先生 來源:公眾號編程珠璣 個人博客:https://www.yanbinghu.com ***************************************/ #include <stdio.h> int a = 5; //文件作用域,外部鏈接屬性,其他文件可通過extern int a的方式使用該文件的a static b = 6; //文件作用域,內部鏈接屬性,即便其他文件也有同名標識符,它們也是不同的 int main(void) { int sum = 0 ; //無鏈接屬性 sum = a + b; printf("sum is %d\n",sum); return 0; }
從代碼中可以看到,a和b都具有文件作用域,a具有外部鏈接屬性,而b具有內部鏈接屬性,sum具有塊作用域,因此無鏈接屬性。
存儲期
實際上作用域和鏈接屬性都描述了標識符的可見性,而存儲期則描述了這些標識符對應的對象的生存期。存儲期,也分下面幾種:
靜態存儲期,程序執行期間一直都在,文件作用域的變量具有靜態存儲期
自動存儲期,它(變長數組除外)從塊開始,到塊末尾,因此,塊作用域的變量具有自動存儲期,它在棧中存儲,需要顯式初始化。
動態分配存儲期,即通過malloc分配內存的變量。它在堆中存儲,需要顯式初始。
線程存儲期,從名字可以知道, 它與線程相關,使用關鍵字_Thread_local聲明的變量具有線程存儲期,它從聲明到線程結束一直存在。
關于初始化,可參考《C語言入坑指南-被遺忘的初始化》。
同樣地,我們通過下面的代碼來更好地理解存儲期
/**************************** 作者:守望先生 來源:公眾號編程珠璣 個人博客:https://www.yanbinghu.com ***************************************/ #include <stdio.h> int num1 = 222; //靜態存儲期 static int num2 = 111; //靜態存儲期 int add(int a,int b) { static int tempSum = 0; //靜態存儲期 tempSum = tempSum + a + b; return tempSum; } int main(void) { printf("num1=%d,num2=%d\n",num1,num2); int sum = 0; //自動存儲期 sum = add(num1,num2); printf("first time sum=%d\n",sum);//sum = 333 sum = add(num1,num2); printf("second time sum=%d\n",sum); //sum = 666 return 0; }
另外,如果我們通過nm命令查看編譯出來的程序文件的符號表,我們可以找到num1,num2,tempSum,而沒有sum,前者所用的內存數量在編譯時就確定了。
$ gcc -g -o lifetime lifetime.c $ nm lifetime|grep num1 0000000000601038 D num1 $ nm lifetime|grep num2 000000000060103c d num2 $ nm lifetime|grep tempSum 0000000000601044 b tempSum.2289 $ nm lifetime|grep sum $
什么全局變量,局部變量,靜態局部變量,靜態全局變量
到這里,我們就可以很容易區分上面的變量類型了。實際上這里只是換了一種說法:
全局:具有文件作用域的變量
靜態:具有靜態存儲期或內部鏈接屬性
局部:具有函數或塊作用域的變量
因而結合起來,也就很好理解了。
局部變量:函數或塊作用域的變量
靜態局部變量:函數或塊作用域,靜態存儲期
全局變量:具有文件作用域的變量
靜態全局變量:內部鏈接屬性的,具有文件作用域的變量
當然,這僅僅是為了區分它們,這并不是它們的嚴格定義。更好的方法,是通過代碼來理解:
#include <stdio.h> int num1 = 222; //全局變量 static int num2 = 111; //靜態全局變量 int add(int a,int b) { static int tempSum = 0; //靜態局部變量 tempSum = tempSum + a + b; return tempSum; } int main(void) { printf("num1=%d,num2=%d\n",num1,num2); int sum = 0; //局部變量 sum = add(num1,num2); printf("first time sum=%d\n",sum);//sum = 333 return 0; }
“怎么區分作用域,存儲器,鏈接屬性”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。