這一篇我們來看看沒有返回,只靠形參、實參傳遞參數的函數,先來學習最簡單的一種:
1、傳值調用(賦值傳遞)
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 void swap(int a,int b) 6 { 7 int temp; 8 printf("交換前,a=%d b=%d \n",a,b); 9 temp=a; 10 a=b; 11 b=temp; 12 printf("交換后,a=%d b=%d \n",a,b); 13 } 14 int main() 15 { 16 int x=10,y=20; 17 printf("交換前,x=%d y=%d \n",x,y); 18 swap(x,y); 19 printf("交換后,x=%d y=%d \n",x,y); 20 return 0; 21 }
從運行程序結果可以看出:被調函數swap只對形參操作,實參無變化。顯然,傳值調用屬於單向值傳遞,函數運行結果不影響、不改變調用函數的實參。
看過了最簡單的,下面來看看復雜一點兒的————引用調用(指針傳遞、賦地址傳遞)。C語言中,這個詞也被叫做“指針傳遞”、“賦地址傳遞”。在C++語言中,則有另外的含義了。用起來,也會方便許多,但是,C語言不支持。引用調用(指針傳遞、賦地址傳遞)分為同級指針傳遞,和二級指針傳遞2種方式。同時,根據主調函數是否申請內存,又分兩種情況來分析。下面先來看:
2、主調函數申請內存條件下,同級指針傳遞:
2.1 主調函數申請內存,同級指針傳遞(傳遞變量地址)
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 void swap(int * a,int *b) 6 { 7 int temp; 8 printf("a地址:%p b地址:%p \n",a,b); 9 printf("交換前,a=%d b=%d \n",*a,*b); 10 temp=*a; 11 *a=*b; 12 *b=temp; 13 printf("交換后,a=%d b=%d \n",*a,*b); 14 } 15 int main() 16 { 17 int x=10,y=20; 18 printf("x地址:%p y地址:%p\n",&x,&y); 19 printf("交換前,x=%d y=%d \n",x,y); 20 swap(&x,&y); 21 printf("交換后,x=%d y=%d \n",x,y); 22 return 0; 23 }
本例中,main主函數申請了2個變量——x和y,並把它們的地址——&x和&y傳遞給被調函數swap,被調函數的形參定義了2個指針變量int *a,int *b來接收。由於a,b,&x,&y都是地址,相當於a=&x,b=&y。
如果把&x和&y也看成指針,則有*a=*(&x)=x,*b=*(&y)=y。顯然,對其解引用后,所得值為變量的值,而不是地址。所以稱之為同級指針傳遞(因為a,b和&x,&y都是只需一次解引用操作即可求得變量的值,我認為稱之為“一級指針傳遞”更為貼切)。
從程序運行結果來看,被調函數修改了實參的值。所以,我們能得出結論:主調函數申請內存,同級指針傳遞實參變量地址,被調函數能夠修改實參的值。
下面我們來看看堆和棧上字符串地址的傳遞:
2.2 主調函數申請內存,同級指針傳遞(傳遞堆上、棧上字符串地址)
1 void func(char* p) 2 { 3 strcpy(p, "Hello,World!"); 4 } 5 void test01() 6 { 7 //分配到棧上 8 char buf[1024] ="I like C語言!"; 9 printf("棧上初始字符串:%s \n", buf); 10 func(buf); 11 printf("主調函數申請內存,同級指針傳遞,棧上字符串返回:%s \n", buf); 12 } 13 14 void printString(char* str) 15 { 16 strcpy(str, "Hello,World!"); 17 printf("主調函數申請內存,同級指針傳遞,堆上字符串返回:%s \n", str); 18 } 19 void test02() 20 { 21 //分配到堆區 22 char* p = malloc(sizeof(char) * 64); 23 memset(p, 0, 64); 24 strcpy(p, "I like C語言!"); 25 printf("堆上初始字符串:%s \n", p); 26 printString(p); 27 printf("字符串長度=%d \n", strlen(p)); 28 } 29 30 int main() 31 { 32 test01(); 33 test02(); 34 system("pause"); 35 return EXIT_SUCCESS;
}
這里,由於我們可以把字符串名“buf”和指針“p”看作地址,顯然,這也是同級指針傳遞。並且,通過程序運行結果,我們可以得出結論:主調函數申請內存,同級指針傳遞字符串地址,被調函數能夠對堆和棧上字符串進行賦值,修改操作。
下面再來看一段代碼:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 void buildString(char* pp) 6 { 7 char* temp = "被調函數修改字符串"; 8 pp = temp; 9 printf("%s \n", *pp); 10 } 11 void test01() 12 { 13 char* p = "主調函數定義初始字符串"; 14 printf("%s \n", p); 15 buildString(p); 16 printf("%s \n", p); 17 } 18 19 int main() 20 { 21 test01(); 22 return 0; 23 }
上面這段代碼無法正常運行,再次證明了一個事實:以指針形式聲明的字符串,保存在程序數據區,不能被改動。
通過上面3個例程,我們可以得出結論:主調函數申請內存,同級指針傳遞地址(無論是變量地址還是堆或者棧的地址)條件下,被調函數能夠修改實參的值。當然,程序數據區的字符串依然不能修改!
2.3 在這里,我們提出一個問題:如果,在主調函數申請內存,二級指針傳遞地址(無論是變量地址還是堆或者棧的地址,甚至是程序數據區變量、常量地址)條件下,會發生什么那?也許,這將是我們下一篇隨筆的命題!
3、被調函數申請內存,同級指針傳遞地址
3.1 被調函數申請內存,同級指針傳遞變量地址,這種情況不存在。或者說我不知道如何用C語言來就這種情況編程。
3.2 被調函數申請內存,同級指針傳遞字符串地址
1 #define _CRT_SECURE_NO_WARNINGS 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <string.h> 5 6 void getString01(char* pp) //傳遞數組地址 7 { 8 char sTring[] = "Hello,world!"; 9 printf("字符串數組地址是:%p \n", sTring); 10 pp = sTring; 11 } 12 void test01() 13 { 14 char* p = NULL; 15 getString01(p); 16 printf("返回數組地址得到: %p, p=%s \n\n", p, p); 17 } 18 19 void getString02(char* pp) //傳遞指針地址 20 { 21 char* pOint = "Hello,Kitty!"; 22 printf("字符串指針地址是:%p \n", pOint); 23 pp = pOint; 24 } 25 void test02() 26 { 27 char* p = NULL; 28 getString02(p); 29 printf("返回指針地址得到:%p, p= %s \n\n", p, p); 30 } 31 32 void getString03(char* pp) //傳遞堆地址 33 { 34 char* hEap = malloc(20 * sizeof(char)); 35 if (hEap == NULL) return; 36 memset(hEap, 0, 20 * sizeof(char)); 37 strcpy(hEap, "Hello,Miss!"); 38 pp = hEap; 39 printf("堆地址是: %p \n", pp); 40 } 41 void test03() 42 { 43 char* p = NULL; 44 getString03(p); 45 printf("返回堆地址得到: %p, p=%s \n", p, p); 46 } 47 int main(void) 48 { 49 test01(); //調用函數,得到輸出:“傳遞同級指針得:(null)” 50 test02(); 51 test03(); 52 system("pause"); 53 return EXIT_SUCCESS; 54 }
運行上面例程,結論是: 被調函數無論是在堆上,還是在棧上,還是以指針形式在程序數據區申請內存,其對形參的定義都無法傳遞給實參。
3.3 被調函數申請內存,二級指針傳遞字符串地址
1 #define _CRT_SECURE_NO_WARNINGS 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <string.h> 5 6 void getString01(char** pp) //傳遞數組地址 7 { 8 char sTring[] = "Hello,world!"; 9 printf("字符串數組地址是:%p \n", sTring); 10 *pp = sTring; 11 } 12 void test01() 13 { 14 char* p = NULL; 15 getString01(&p); 16 printf("返回數組地址得到: %p, p=%s \n\n", p, p); 17 } 18 19 void getString02(char** pp) //傳遞指針地址 20 { 21 char* pOint = "Hello,Kitty!"; 22 printf("字符串指針地址是:%p \n", pOint); 23 *pp = pOint; 24 } 25 void test02() 26 { 27 char* p = NULL; 28 getString02(&p); 29 printf("返回指針地址得到:%p, p= %s \n\n", p, p); 30 } 31 32 void getString03(char** pp) //傳遞堆地址 33 { 34 char* hEap = malloc(20 * sizeof(char)); 35 if (hEap == NULL) return; 36 memset(hEap, 0, 20 * sizeof(char)); 37 strcpy(hEap, "Hello,Miss!"); 38 *pp = hEap; 39 printf("堆地址是: %p \n", pp); 40 } 41 void test03() 42 { 43 char* p = NULL; 44 getString03(&p); 45 printf("返回堆地址得到: %p, p=%s \n", p, p); 46 } 47 int main(void) 48 { 49 test01(); //調用函數,得到輸出:“傳遞同級指針得:(null)” 50 test02(); 51 test03(); 52 system("pause"); 53 return EXIT_SUCCESS; 54 }
運行上面例程,結論是: 被調函數在堆上,或者以指針形式在程序數據區申請內存,其對形參的定義都可以傳遞給實參。在棧上定義的字符串數組,由於棧上變量的生命周期的原因,無法正確傳遞給實參。