一:
python代碼:
a = 2 def b(): print a a = 4 print a b()
在b函數中,有a=4這樣的代碼,說明a是函數b內部的局部變量,而不是外部的那個值為2的全局變量a,那么第一行代碼就報錯了,原因是local variable 'a' referenced before assignment
而在c中就不會報錯:
#include <stdio.h> int a=2; main(){ printf("global variable a : %d\n",a); int a=3; printf("local variable a : %d\n",a); } /*結果: global variable a : 2 local variable a : 3 */
在main函數中,變量a開始為全局變量,后來為本地變量,這在Python中是不允許的
二:
在python中是正確的:
def b(): print x x=3 b()
但是在c中在編譯階段就會報錯:
#include <stdio.h> b(){ printf("b : %d\n",a); } int a =3; main(){
b(); }
錯誤在第三行,錯誤原因是變量a未聲明,如果將上述代碼改一下,改成:
#include <stdio.h> extern int a; b(){ printf("b : %d\n",a); } main(){ b(); }
則雖然編譯通過了,但是鏈接的時候卻報錯了,extern int a;的意思是,變量a在別處有定義,暫時先讓我編譯通過。於是雖然編譯階段是通過了,但等到鏈接的時候,由於本源文件沒有include其他源文件,導致連接器最終還是沒能找到變量a的定義,於是就報錯了
以上是python和c語言不同之處,下面再說一個相同的地方,大家都知道python是有閉包存在的:
a=2 def aa(): print a def bb(): a=4 aa() bb()#打印2
在c語言中同樣存在:
#include <stdio.h> int a=2; b(){ printf("b : %d\n",a); } main(){ int a=4; b();//打印2 } /*但是如果把main函數改為: main(){ a=4; b();//將不在打印4,而是2. }*/
明白了上述問題,我們來看幾個例子:
現在同一文件夾下有如下文件:
編譯a.c: cc -c a.c; 生成a.o
編譯b.c: cc -c b.c; 生成b.o
鏈接a.o,b.o生成最終可執行文件a: cc -o a a.o b.o
//a.c #include <stdio.h> #include "b.h" main(){ int a = 3; bb(); } //b.h void bb(void); //b.c #include <stdio.h> int a = 1; bb(){ printf ("%d\n",a); }
執行./a,執行結果:打印的是1,而不是3
在本例中,b.c中的a屬於公共的通用的變量,b函數里的a也正是這個公共變量a。任何源文件的代碼在引用這個變量之前,只要有這個變量的聲明,或者該源文件include的文件有這個變量的聲明,那么這個源文件就可以訪問這個變量或者修改這個變量的值。本例中,a.c文件就沒有聲明該變量。
//a.c #include <stdio.h> #include "b.h" main(){ int a = 3; bb(); } //b.h int a; void bb(void); //b.c #include <stdio.h> int a = 1; bb(){ printf ("%d\n",a); }
上面這個是在b的頭文件中加入了變量a的聲明。a程序運行的結果還是打印1,而不是3.因為雖然全局公共變量a雖然被帶到了源文件a.c中了,但是在mian函數中的a是局部變量a,而不是全局公共變量a,而bb函數中的a是全局公共變量a,所以打印1.
//a.c #include <stdio.h> #include "b.h" main(){ a = 3; bb(); } //b.h int a; void bb(void); //b.c #include <stdio.h> int a = 1; bb(){ printf ("%d\n",a); }
上面這個例子中,修改了一下main函數,這時,程序a運行的結果才變為了3,這時因為main函數中的a正是全局變量a,並且把a原來的值改為了3,所以打印3
//a.c #include <stdio.h> #include "b.h" main(){ a = 3;
printf("%d",a); bb(); } //b.h int a; void bb(void); //b.c #include <stdio.h> static int a = 1; bb(){ printf ("%d\n",a); }
上面這個例子中,修改了一下b.c,將變量a由原來的全局公共變量變成了本地靜態變量,那么這個變量的作用域僅僅是b.c,我們知道include只是將文件加載進來,
所以,a.c源文件完全等價於:
//a.c #include <stdio.h> int a; void bb(void); main(){ a = 3; printf("%d",a); bb(); }
所以a.c中的變量a是全局公共變量a,而main函數中的a也正是這個,而bb中的a是b.c文件中的本地靜態變量a,所以才會先打印3,后打印1
但是上面這個例子雖然語法上是沒有任何錯誤的,但是卻失去了頭文件庫文件的意義。
正確的寫法是:
//a.c #include <stdio.h> #include "b.h" main(){ printf("%d",b); bb(); } //b.h int b; void bb(void); //b.c #include <stdio.h> #include "b.h" static int a = 1; int b=4; bb(){ printf ("%d\n",a); }
其中a.c可以說成主程序文件,而b.h是這個主程序文件正常運行所依賴的頭文件,而b.c是這個主程序文件正常運行所依賴的庫文件。
我們把b.c這個庫文件中,所有的需要被其他程序引用的變量或函數或者其他什么的,都列出來,做成一個文件,這個文件就是b.c的頭文件,特別要注意到,由於
b.c中的變量a是本地私有的變量,其他程序文件用不到這個變量,所以就沒有出現在頭文件b.h中
還有就是,b.c是要包含他自己的頭文件b.h的,雖然不包含也不會報錯,但這是一個很好的習慣,這樣我們在編譯b.c的時候,編譯器就能幫我們檢測頭文件中的聲明與庫文件是否保持一致,試想一下,如果b.h中,我們寫的是float int b;那么有了include這句話,在編譯b.c的時候編譯器就能幫我們找出錯誤