您好,登錄后才能下訂單哦!
這篇文章主要講解了“C語言中的作用域、鏈接、存儲期的概念”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“C語言中的作用域、鏈接、存儲期的概念”吧!
C 程序中出現的每個標識符都僅在一些可能不連續的部分可見(即可使用),這些部分被稱為其作用域。作用域描述程序中可訪問標識符的區域。
C 擁有四種作用域:
塊作用域
函數作用域
函數原型作用域
文件作用域
任何在復合語句,包含函數體或出現于 if 、 switch 、 for 、 while 或 do-while 語句中的任何表達式、聲明或語句 (C99 起),或在函數定義內的參數列表中聲明的標識符的作用域,在聲明點開始,在聲明于其中的塊或語句的結尾結束。
聲明于函數內部的標號(且只有標號),在該函數中的所有位置(所有嵌套塊中,其自身聲明前后)都在作用域內。注意:任何語句前的冒號字符前的標識符,若不用于其他用途,則隱式聲明一個標號。
void f() { { goto label; // label 在作用域中,盡管之后才聲明 label:; } goto label; // 標號忽略塊作用域 } void g() { goto label; // 錯誤: g() 中 label 不在作用域中 }
非函數定義的函數聲明的參數列表中引入的名稱的作用域,在函數聲明器的結尾結束。
int f(int n, int a[n]); // n 在作用域中并指代第一參數
在任何塊或參數列表外聲明的任何標識符的作用域,在聲明點開始,翻譯單元尾結束。
int i; // i 的作用域開始 static int g(int a) { return a; } // g 的作用域開始(注意 "a" 擁有塊作用域) int main(void) { i = g(2); // i 和 g 在作用域中 }
若相同標識符所命名的二個不同實體在同一時刻都在作用域中,且它們屬于同一命名空間,則作用域被嵌套(不允許其他形式的作用域重疊),而內層作用域中的聲明隱藏外層作用域中的聲明:
// 此處的命名空間為通常標識符。 int a; // 名稱 a 的文件作用域始于此 void f(void) { int a = 1; // 名稱 a 的塊作用域始于此;隱藏文件作用域的 a { int a = 2; // 內層 a 的作用域始于此,隱藏外層 a printf("%d\n", a); // 內層 a 在作用域中,打印 2 } // 內層 a 的塊作用域終于此 printf("%d\n", a); // 外層 a 在作用域中,打印 1 } // 外層 a 的作用域終于此 void g(int a); // 名稱 a 擁有函數原型作用域;隱藏文件作用域的 a
注意,若聲明中有多個或嵌套聲明器,則作用域在最近的外圍函數聲明器的結尾結束:
void f ( // 函數名 'f' 在文件作用域 long double f, // 標識符 'f' 現在在作用域中,隱藏文件作用域的 'f' char (**a)[10 * sizeof f] // 'f' 指代第一參數,它在作用域中 ); enum{ n = 3 }; int (*(*g)(int n))[n]; // 函數參數 'n' 的作用域在其函數聲明器的結尾結束 // 數組聲明器中,全局 n 在作用域 // (這聲明指向返回 3 個 int 的數組的指針的函數的指針)
結構體、聯合體及枚舉標簽的作用域,在聲明該標簽的類型指定符中的標簽出現后立即開始。
struct Node { struct Node* next; // Node 在作用域中并指代此 struct };
枚舉常量的作用域,在枚舉項列表中其定義枚舉項的出現后立即開始。
enum { x = 12 }; { enum { x = x + 1, // 新 x 在逗號前不在作用域中,初始化 x 為 13 y = x + 1 // 新枚舉項 x 現在在作用域中,初始化 y 為 14 }; }
任何其他標識符的作用域,正好在其聲明器結束后和初始化器前開始,若存在初始化器:
int x = 2; // 第一個 'x' 的作用域開始 { int x[x]; // 新聲明的 x 的作用域在聲明器 ( x[x] )后開始。 // 在聲明器內,外層 'x' 仍在作用域中。 // 這聲明 2 個 int 的 VLA 數組。 } unsigned char y = 32; // 外層 'y' 的作用域開始 { unsigned char y = y; // 內層 'y' 的作用域在初始化器( = y )前開始 // 這不會以值 32 初始化內層 'y' , // 這以其自身的不確定值初始化內層 'y' } unsigned long factorial(unsigned long n) // 聲明器結束, 'factorial' 從此點開始在作用域中 { return n < 2 ? 1 : n * factorial(n - 1); // 遞歸調用 }
鏈接指的是在其他作用域指代一個標識符(具名對象或函數)的能力。若在數個作用域中聲明有同一標識符的對象或函數,但不能從所有這些作用域指代它們,則會創建數個對象的實例。
C 變量具有3種鏈接屬性:
外部鏈接
內部鏈接
無鏈接
只能從其所在的作用域指代該標識符。
所有函數參數
所有非 extern 的塊作用域對象(包含聲明為 static 者)
能從當前翻譯單元的所有作用域指代該標識符。在一個翻譯單元內,帶內部鏈接的標識符的每個實例均表示相同的標識符或函數。 內部鏈接的標識符對于翻譯單元是唯一的。
所有 static 文件作用域標識符(函數和對象)
能從整個程序的任何其他翻譯單元指代該標識符。具有外部鏈接的標識符的名稱指定相同的函數或數據對象,這與具有外部連接的相同名稱的任何其他聲明一樣。 這兩個聲明可以在同一個翻譯單元中,也可以在不同的翻譯單元中。 如果該對象或函數還具有全局生存期,則該對象或函數由整個程序共享。
所有非 static 函數
所有 extern 對象(除非之前聲明為 static )
所有文件作用域的非 static 對象擁有此鏈接。
若同一標識符在同一翻譯單元中一同帶內部和外部鏈接出現,則行為未定義。這在使用試探性定義時有可能。
每個對象都有稱為存儲期的屬性,它限制對象的生存期。 C 中有四種存儲期:
自動存儲期。
靜態存儲期。
線程存儲期。
分配存儲期。
如果對象具有自動存儲期,那么它存在于,程序進入定義這些變量的塊到退出這個塊。進入聲明對象于其中的塊時分配其存儲,而在以任何方式( goto 、 return 、抵達結尾)退出該塊時解分配存儲。
一個例外是 VLA ;在執行聲明時而非塊入口分配其存儲,并在聲明離開作用域而非退出塊時解分配存儲。 (C99 起)若遞歸地進入塊,則對每個遞歸層進行新的分配。
以下變量具有自動存儲期:
非 static 塊作用域對象
所有函數參數
用于塊作用域的復合字面量
如果對象具有靜態存儲期,那么它在程序執行期間一直存在。存儲期是整個程序的執行過程,只在 main 函數之前初始化一次存儲于對象的值。
以下變量具有靜態存儲期:
所有聲明為 static 對象
所有帶內部或外部鏈接且不聲明為 _Thread_local (C11 起)的對象都擁有此存儲期。
存儲期是創建對象的線程的整個執行過程,在啟動線程時初始化存儲于對象的值。每個線程擁有其自身的相異對象。若執行訪問此對象的表達式的線程,不是執行其初始化的線程,則行為是實現定義的。
以下變量具有靜態存儲期:
所有聲明為 _Thread_local 的對象(C11 起)
按照請求,用動態內存分配函數分配和解分配存儲。
感謝各位的閱讀,以上就是“C語言中的作用域、鏈接、存儲期的概念”的內容了,經過本文的學習后,相信大家對C語言中的作用域、鏈接、存儲期的概念這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。