C語言使用return關鍵字返回函數值,可以很好對函數做封裝,此處的疑問是:函數內部創建的變量都是局部變量,即私有的,作用域就在函數之內,為什么卻可以把值傳給調用函數?
解釋這個問題還需要從C語言調用函數傳參類比來說,C語言傳參調用時,可以采用傳值和傳指針兩種方式。
傳值的形式:只是將參數值的拷貝傳給函數,並非參數本體,如:
1 int test_func(int i) 2 { 3 i++; 4 printf("Function i : %d\n", i); 5 return 0; 6 } 7 8 int main() 9 { 10 int a = 10; 11 printf("Main Pre: %d\n", a); 12 test_func(a); 13 printf("Main Now: %d\n", a); 14 15 return 0; 16 }
傳指針形式:直接傳給函數的是變量的地址,由於被調函數在參數指針的作用域之內,此時直接改變變量的本體。
1 int test_func(int *i) 2 { 3 (*i)++; //注意:++的優先級比*高 4 printf("Function i : %d\n", *i); 5 return 0; 6 } 7 8 int main() 9 { 10 int a = 10; 11 printf("Main Pre: %d\n", a); 12 test_func(&a); 13 printf("Main Now: %d\n", a); 14 15 return 0; 16 }
同理,函數返回也有兩種形式。
1、函數返回變量值
此時,返回變量值的方式與函數調用傳值同樣的道理,在函數結束返回時,將局部變量值拷貝給一個臨時變量,然后將這個臨時變量返回給調用函數。因此,即使局部變量在返回時已經釋放內存,也不影響返回的變量值。
1 int test_func() 2 { 3 int i = 2; 4 printf("Function i : %d\n", i); 5 return i; 6 } 7 8 int main() 9 { 10 int a = 0; 11 a = test_func(); 12 printf("Main Now: %d\n", a); 13 14 return 0; 15 }
從匯編的角度來看源代碼:
由以上看出:返回變量值的時候,直接將局部變量的值傳給了了寄存器eax,也就是說,函數返回以后,雖然局部變量已被釋放,但是eax里面的還有一個值的拷貝。
2、函數返回地址
此時注意:C語言的指針操作,大部分都是直接對指針指向的變量直接操作,函數內部的變量和指針一般分配在棧上,而棧上的數據都是臨時保存的,當函數返回時會自動釋放掉,因此如果直接返回一個棧上的指針,返回的值將不可預知。
1 int *test_func() 2 { 3 int local_data = 10; 4 5 printf("Function local_data : %d\n", local_data); 6 7 return &local_data; 8 } 9 10 int main() 11 { 12 int *main_data = NULL; 13 14 main_data = test_func(); 15 16 printf("Return data: %d\n", *main_data); 17 18 return 0; 19 }
從匯編語言角度查看源碼:
由以上看出:返回指針的時候,用的是指令lea,這條指令的作用是,將[ebp-4](此單元對應的是變量local_data在棧上的數據存儲位置)這個數據單元的地址傳給eax寄存器,但是像這樣在棧上開辟出來臨時存儲數據的單元,只要調用函數結束,就會釋放掉里面的數據,因此雖然返回了一個指針,指針指向的數據卻已經被系統銷毀了,這就導致返回的指針指向不可預知的數據。