C語言中變量和函數的作用域和鏈接屬性
作用域
-
代碼塊作用域: 代碼塊指的是使用“{}”包圍起來的部分。
- 在代碼塊中定義的變量,代碼塊之外是不能訪問的。
- 代碼塊嵌套之后的變量作用域,子代碼塊中定義的同名變量會覆蓋父代碼塊中的同名變量。
-
#include <stdio.h> int main(){ { int i=0; printf("%d\n",i);//0 { int i=1; printf("%d\n",i);//1 } } }
-
函數原型作用域: 函數原型作用域僅包括函數原型形式參數所在的括號。該作用於主要強調聲明函數時不能使用相同名稱的形式參數。
-
函數作用域: 函數作用域指的就是函數體的部分。K&R C的函數體不包含函數參數,所以在函數體內定義和函數參數同名變量會替代函數參數。ANSI C修改了該問題,函數參數在函數體的最外層作用域之內。
- 函數作用域內定義的變量,在函數之外不能進行訪問。
-
文件作用域: 源文件所在的范圍。
- 所有代碼塊之外定義的標識符就有文件作用域。(全局變量)
- 函數名不屬於任何任何代碼塊,因此也具有文件作用域。
鏈接屬性
- 當組成一個程序的所有源文件被編譯之后,所有目標文件以及那些從一個或多個函數庫中引用的函數鏈接在一起,組成可執行文件。問題是當相同的標識符出現在不同的源文件時該怎么辦?標識符的鏈接屬性用來處理不同源文件中出現的標識符。標識符的鏈接屬性和它的作用域有關但並不相同。
-
none(無): 總是被當做單獨個體,也就是說該標識符在不同源文件中的多次聲明之間毫無關系,分屬不同的實體。
- 代碼塊之內定義的變量默認具有none連接屬性。通過extern修飾符可以修改為外部鏈接屬性,這樣就可以訪問其他源文件中定義的外部變量了。static修飾符不能修改連接屬性和作用域,只修改存儲類型。
#include <stdio.h> int main(){ { static int i=0; printf("%d\n",i);//0 } printf("%d\n",i);//error: ‘i’ undeclared (first use in this function) }
- 代碼塊之內定義的變量默認具有none連接屬性。通過extern修飾符可以修改為外部鏈接屬性,這樣就可以訪問其他源文件中定義的外部變量了。static修飾符不能修改連接屬性和作用域,只修改存儲類型。
-
internal(內部): 在同一個源文件中的所有聲明指向同一實體,不同源文件指向不同實體。全局變量和函數可以通過static修飾符從外部鏈接屬性改為內部鏈接屬性。
-
external(外部):在所有源文件中指向同一實體。全局變量和函數默認具有全局作用域,可以通過extern將從未顯式聲明鏈接屬性的標識符修改為外部鏈接屬性。
- static關鍵字可以將全局變量和函數修改為內部連接屬性。
- 全局變量默認為外部鏈接屬性:
main.c #include <stdio.h> int i; int main(){return 0;}; test.c #include <stdio.h> int i=0; gcc -c main.c test.c //success gcc -o main main.o test.o //multiple definination of "i" main.c修改: #include <stdio.h> extern int i;//通過使用extern將該全局變量聲明為外部鏈接屬性,使用外部定義的變量。 int main(){return 0;} gcc -o main main.o test.o //success
- 函數通過extern關鍵字修飾為全局連接屬性,指的是該函數可能在其他源文件中進行定義。
main.c #include <stdio.h> extern int printHello();//僅指明該函數是在外部函數中聲明定義。 int main(){ printHello(); return 0; } test.c #include <stdio.h> int printHello(){ printf("hello\n"); return 0; }