“我們是傘兵,本來就該被包圍的!”----《兄弟連》。
在戰爭中,傘兵天生就該被包圍,而在編程語言中,函數生來就該被調用。在被調用的過程中,執行函數的指令,完成值和參數的傳遞。按照不同的傳遞方式,函數可以分為下面幾類:
1、先來看返回變量、常量的函數:
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 int func(int a) 5 { 6 a=2*a*a; 7 printf("a=%d\n",a); 8 return a; 9 } 10 int main() 11 { 12 int b=func(10); 13 printf("b=%d\n",b); 14 return 0; 15 }
上面的函數是返回變量的值,如果把被調函數func中的變量a換成常量,程序依然能夠得到正確結果。例如:
1 #include <stdio.h> 2 3 int func() 4 { 5 const char a='W'; 6 printf("A=%c \n",a); 7 return a; 8 } 9 int main() 10 { 11 char b=func(); 12 printf("b=%c \n",b); 13 return 0; 14 }
也許有人會問,辛辛苦苦敲了半天代碼,就返回了一個字符,為什么不返回一個字符串那?返回字符串不是不行,可要返回字符串,就不能按照返回一般的變量、常量來處理了。我們知道,在C語言編程環境下,字符串只能用字符型數組或者字符型指針來聲明和定義,不存在C++語言語言環境下的string類型。因此,當你要返回字符串的時候,其實需要返回的是字符串的地址。這就引出了我們的下一個課題:
2、返回字符串地址
函數不僅能返回值,還能返回地址。返回地址時,需要在函數的返回值數據類型前面加”*“,下面看例程:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 char* getString() 6 { 7 char str[] = "hello,world"; 8 return str; //返回數組地址 9 } 10 void test02() 11 { 12 char* p = NULL; 13 p = getString(); //接收字符串數組地址 14 printf("test02返回字符串數組: %s \n", p); 15 /* 因為p接收的是被返回的字符串數組的地址。而字符串數組在棧上, 當getString函數 16 執行完以后,str字符串已經被內存釋放了,test02函數調用它的結果就不確定了。 */ 17 } 18 19 int main() 20 { 21 test02(); 22 return 0; 23 }
運行程序,我們會發現,字符串數組沒有被正確輸出。下面再看一例:
3、局部變量地址的返回
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 int* func() 6 { 7 int a = 10; 8 return &a; 9 } 10 void test01() 11 { 12 int* p = func(); //這種調用,結果已經不重要了,因為a的內存(因該是a所指向的內存) 13 //被系統釋放了,我們沒有權限區操作這塊內存 14 printf("test01第一次:a=%d \n", *p); //第一次輸出‘10’,因為系統默認為作者保留這段內存 15 printf("test02第二次:a=%d \n", *p); //第二次輸出內容就不定了,系統已經將這段內存釋放了 16 } 17 18 int main() 19 { 20 test01(); 21 return 0; 22 }
如果我們用VS2015以上編輯器運行上面代碼,控制台會輸出注解的內容。如果用codeblock或者其他編輯器運行,則有可能無任何輸出,因為你輸出的內存數據是無效的。綜合起來看,函數不能返回在被調函數中定義的局部變量、數組形式聲明的字符串(字符串數組)。因為他們存放在棧上。隨着被調函數運行結束,這些局部變量、字符串等占用的內存被釋放。再訪問上述內存空間就是非法訪問了。那么是不是函數就不能返回地址了那?不是!函數可以返回存放在程序數據區、堆區的地址(關於內存分區的內容參見:https://www.cnblogs.com/GoldCrop/p/11030984.html)。詳見下面例子:
4、堆及程序數據區地址的返回
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 char* getString01() 6 { 7 char *str= malloc(64); //在堆上定義字符串 8 memset(str,0,64); 9 strcpy(str,"hello,world"); 10 return str; //返回字符串地址 11 } 12 void test02() 13 { 14 char *q = getString01(); //接收字符串地址 15 printf("堆上字符串內容 :%s \n", q); /* 因為字符串地址存放在堆區,即使getString01 16 運行完后,字符串所在內存也不會被釋放,可以返回字符串地址 */ 17 } 18 19 char* getString02() 20 { 21 char* str = "hello,world!"; //聲明並定義字符串指針變量,字符串存儲在數據區 22 return str; //返回指針地址 23 } 24 void test03() 25 { 26 char* p = NULL; 27 p= getString02(); 28 printf("程序數據區字符串內容 :%s \n", p); 29 } 30 int main() 31 { 32 test02(); 33 test03(); 34 return 0; 35 }
通過上面的例子可以看出,我們能返回程序數據區、堆區變量、常量的地址,但不能返回棧區的變量、常量的地址。