指針做函數參數,指針做函數返回類型
有時候我們可以使用函數的返回值來回傳數據,在簡單的情況下是可以的,但是如果返回值有其它用途(例如返回函數的執行狀態量),或者要回傳的數據不止一個,返回值就解決不了了,所以要引用上指針來傳遞。
指針做函數參數:
在C語言中,函數的參數不僅可以是整數、小數、字符等具體的數據,還可以是指向它們的指針。用指針變量作函數參數可以將函數外部的地址傳遞到函數內部,使得在函數內部可以訪問到函數外部的數據,并且這些數據不會隨著函數的結束而被銷毀。像數組、字符串、動態分配的內存等都是一系列數據的集合,沒有辦法通過一個參數全部傳入函數內部,只能傳遞它們的指針,在函數內部通過指針來影響這些數據集合。有的時候,對于整數、小數、字符等基本類型數據的操作也必須要借助指針,一個典型的例子就是交換兩個變量的值:
?
#include void swap(int a, int b){ int temp; //臨時變量 temp = a; a = b; b = temp; } int main(){ int a = 66, b = 99; swap(a, b); printf("a = %d, b = %d ", a, b); return 0; } /* 從結果可以看出,a、b 的值并沒有發生改變,交換失敗。 這是因為 swap() 函數內部的 a、b 和 main() 函數內部的 a、b 是不同的變量, 占用不同的內存,它們除了名字一樣,沒有其他任何關系,swap() 交換的是它內部 a、b 的值, 不會影響它外部(main() 內部) a、b 的值。他們會隨著函數段的結束而失去了作用域
?
利用指針:
?
#include void swap(int *p1, int *p2){//這里接收到的是a和b的地址 int temp; temp = *p1;//利用地址將值做修改 *p1 = *p2; *p2 = temp; } int main(){ int a = 66, b = 99; swap(&a, &b);//這里將a和b的地址獲取傳入 printf("a = %d, b = %d ", a, b); return 0; } //函數運行結束后雖然會將 p1、p2 銷毀,但它對外部 a、b 造成的影響是“持久化”的, 不會隨著函數的結束而“恢復原樣”。因為我們對它做的是進入到地址的修改
?
用數組做函數參數:
如果一個函數按值傳遞數組,則必須分配足夠的空間來存儲原數組的副本,然后把原數組的所有數組拷貝到新的數組中去,如果把數組的地址傳遞給函數,讓函數來直接處理原來數組則效率要高。
但是 傳遞地址的時候,總會導致一些問題,C通常安值傳遞數據,因為這樣做可以保證數據的完整性,如果函數使用的是原始的數組的副本,就不會發生修改原始數據,但是,處理數組的函數通常都需要使用原始數據,因此這樣的函數可以修改原數組,有時,這正是我們需要的:void add(double a[ ],int n,int b);? 調用此函數,將可以將原來數組的值進行修改,也可以說是函數通過指針,直接將原數組做修改了
數組是一系列數據的集合,無法通過參數將它們一次性傳遞到函數內部,如果希望在函數內部操作數組,必須傳遞數組指針。下面的例子定義了一個函數 max(),用來查找數組中值最大的元素:
?
#include int max(int len, int a[]);//聲明 注意這里的a[ ] 里邊沒有任何東西,其實也可以放東西也可以不放的 /*實際上這種形式的數組定義都是假象,不管是int a[100]還是int a[]都不會創建一個數組出來,編譯器也不會為它們分配內存,實際的數組是不存在的,它們最終還是會轉換為int *intArr這樣的指針。這就意味著,兩種形式都不能將數組的所有元素“一股腦”傳遞進來,大家還得規規矩矩使用數組指針。*/ //真正傳遞的數組可以有少于或多于 100 個的元素。 int main(void) { int n; int a[100]; int i; scanf("%d", &n); for (i = 0; i < n; i++) { scanf("%d", a+i);//給數組里邊的值初始化 } //這個a的原型是 int *a //所以這里可以使用 &a[i] 或者 a+i printf("max=%d", max(n,&a)); return 0; } int max(int len, int a[]) {//定義 int t = a[0]; int i; for (i = 1; i < len; i++) {//依次比較 if (t < a[i]) { t = a[i]; } } return t;//返回max } 注意 不管使用哪種方式傳遞數組,都不能在函數內部求得數組長度,因為 intArr 僅僅是一個指針, 而不是真正的數組,所以必須要額外增加一個參數來傳遞數組長度。 //有時候,因為把數組傳入函數時傳遞的是地址,所以那個函數內部可以修改數組的值, 為了保護數組的值不被函數修改破壞,可以設置參數為const: int sum (const int a[ ],int b);
?
C語言為什么不允許直接傳遞數組的所有元素,而必須傳遞數組指針呢?
參數的傳遞本質上是一次賦值的過程,賦值就是對內存進行拷貝。所謂內存拷貝,是指將一塊內存上的數據復制到另一塊內存上。
對于像 int 等基本類型的數據,它們占用的內存往往只有幾個字節,對它們進行內存拷貝非常快速。而數組是一系列數據的集合,數據的數量沒有限制,可能很少,也可能成千上萬,對它們進行內存拷貝有可能是一個漫長的過程,會嚴重拖慢程序的效率,為了防止技藝不佳的程序員寫出低效的代碼,C語言沒有從語法上支持數據集合的直接賦值。
指針的函數返回類型:
程序編譯后,每個函數都有執行第一條指令的地址即首地址,稱[函數指針。函數指針即指向函數的指針變量,要間接調用函數可以使用指針變量來實現。
int?(*pf)(int,?int);
通過將pf與括號中的“*”強制組合組合在一起,表示定義的pf是一個指針,然后與下面的“()”再次組合,表示的是該指針指向一個函數,括號里表示為int類型的參數,最后與前面的int組合,此處int表示該函數的返回值。因此,pf是指向函數的指針,該函數的返回值為int。函數指針與返回指針的函數的含義大不相同。函數指針本身是一個指向函數的指針。指針函數本身是一個返回值為指針的函數。
?
#include #include char *strs(char *strl1, char *strl2);//聲明一個char*類型的指針函數 int main(void) { char str1[50];//定義字符串 char str2[50]; char *str;//定義字符指針 gets(str1);//輸入 gets(str2); str = strs(str1, str2); printf("%s", str); return 0; } char *strs(char *strl1, char *strl2) { if (strlen(strl1) > strlen(strl2)) {//比較長度 誰長返回誰 return strl1; } else { return strl2; } }
?
? ? ? ?用指針作為函數返回值時需要注意的一點是,函數運行結束后會銷毀在它內部定義的所有局部數據,包括局部變量、局部數組和形式參數,函數返回的指針請盡量不要指向這些數據,C語言沒有任何機制來保證這些數據會一直有效,它們在后續使用過程中可能會引發運行時錯誤 ,? 但是,這里所謂的銷毀并不是將局部數據所占用的內存全部抹掉,而是程序放棄對它的使用權限,棄之不理,后面的代碼可以隨意使用這塊內存 , 如果使用及時也能夠得到正確的數據,如果有其它函數被調用就會覆蓋這塊內存,得到的數據就失去了意義。而覆蓋它的究竟是一份什么樣的數據我們無從推斷(一般是一個沒有意義甚至有些怪異的值)。
?
#include int *func(){ int n = 100; return &n; } int main(){ int *p = func(), n; n = *p; printf("value = %d ", n);//因為前面沒有覆蓋,所以還是能得到之前的值 return 0; } ******與下面比較:****** #include int *func(){ int n = 100; return &n; } int main(){ int *p = func(), n; printf("c.biancheng.net ");//這里會覆蓋掉 n = *p; printf("value = %d ", n);//所以輸出的值會無從得知 return 0; }
?
還應該注意函數指針變量的調用:
分析函數指針變量不能進行算術運算,這是與數組指針變量不同的。數組指針變量加減一個整數可使指針移動指向后面或數組元素,而函數指針的移動是毫無意義的。函數調用中“(* 指針變量名)”的兩邊的括號不可少,其中的“*”不應該理解為求值運算,在此處只是一種表示符號。要把“z= * pomax(x,y);”改成“z=(*pomax)(x,y);”。
?
評論
查看更多