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