1. 程式人生 > >C語言 指標基礎篇 陣列,函式與指標的運用 2 14

C語言 指標基礎篇 陣列,函式與指標的運用 2 14

下面看看如何在函式中運用指標吧

 下面是往函式傳入指標的簡單操作,不是傳入陣列的。判斷一個a是否大於b是的話給,是的話對其進行操作,不是的話就直接返回。

 1 #include <stdio.h>
 2 int main(){
 3     int num1,num2,*p1,*p2;
 4     p1 = &num1,p2=&num2;
 5     scanf("%d%d",&num1,&num2);
 6 
 7     int fun(int *n1,int *n2);   //我們在宣告函式時候,要定義好“指標變數”
 8
if(*p1<*p2){ 9 fun(p1,p2); //而在傳入值的時候只需要把指標變數傳入進去就行了,這裡沒有*是因為指標變數值需要在宣告的時候加* 10 } 11 printf("max num is:%d\nmin mum is:%d",*p1,*p2); //這裡的*P就是解引用的操作 12 return 0; 13 } 14 int fun(int *n1,int *n2){ //宣告的指標變數和定義的指標變數的名字必須相同 15 int tmp; 16 tmp = *n1; 17 *n1 = *n2;
18 *n2 = tmp; 19 return 0; 20 }

 

下面是函式中在運用傳入指標的時候的錯誤操作

錯誤1:

1 int fun(int *n1,int *n2){      
2     int tmp;
3     tmp = n1;
4     n1 = n2;
5     n2 = tmp;
6     return 0;
7 }

原因:不能達到預期結果,因為這隻交換了p1和p2的值,只是交換了他們的儲存空間,沒有交換內容。並且本身修改後不能傳回實參,即不能傳到main函式,最直接的就是編譯器會直接報錯。

錯誤2:

1 int
fun(int *n1,int *n2){ 2 int tmp*; 3 tmp* = *n1; 4 *n1 = *n2; 5 *n2 = *tmp; 6 return 0; 7 }

原因:上述方式,定義了一個未知空間temp,並且通過指標修改了其資料。如果temp中存的是非常重要的內容,那麼程式就會出問題,所以這種方法也是非常不可取的。但是編譯的時候是不會報錯的。

錯誤3:

1 int fun(int n1,int n2){
2     int tmp;
3     tmp = n1;
4     n1 = n2;
5     n2 = tmp;
6     return 0;
7 }

原因:能傳遞,但是不能輸出交換後的值。

錯誤4:

1 int fun(int *n1,int *n2){
2     int tmp;
3     tmp = n1;
4     n1 = n2;
5     n2 = tmp;
6     return 0;
7 }

原因:這樣根本就沒有呼叫到傳入的值,所以也不會進行操作。

 

 1 #include <stdio.h>
 2 int main(){
 3     int num1,num2,*p1,*p2;
 4     p1 = &num1,p2=&num2;
 5     scanf("%d%d",&num1,&num2);
 6 
 7     int fun(int *n1,int *n2);
 8     if(*p1<*p2){
 9         fun(p1,p2);
10     }
11     printf("max num is:%d\nmin mum is:%d",*p1,*p2); //這裡取的是經過函式操作後的p1和p2,並沒有用返回值。
12     return 0;
13 }
14 int fun(int *n1,int *n2){
15     int tmp;
16     tmp = n1;
17     n1 = n2;
18     n2 = tmp;
19     return 0;
20 }

而在上面你也可以看到,我們並沒有取返回值。但是我們可以獲取到經過函式操作後的結果。 

我們再弄一個例子,輸入三個數。按照大小排列。用指標的方式實現:

 1 #include <stdio.h>
 2 int main(){
 3     int a,b,c,*p1,*p2,*p3;
 4     printf("Please input two num:");
 5     p1 = &a,p2=&b,p3=&c;
 6     scanf("%d %d %d",&a,&b,&c);
 7     int pd(int *num1,int *num2,int *num3);
 8     pd(p1,p2,p3);
 9     printf("%d>%d>%d",*p1,*p2,*p3);
10     return 0;
11 }
12 int pd(int *num1,int *num2,int *num3){
13     int lj(int *n1,int *n2);
14     int i;
15     for (int i = 0; i <3 ; ++i) {
16         if (*num1 < *num2) {
17             lj(num1, num2);
18         } else if (*num1 < *num3) {
19             lj(num1, num3);
20         } else if (*num2 < *num3) {
21             lj(num2, num3);
22         }
23     }
24     return 0;
25 }
26 int lj(int *n1,int *n2){
27     int tmp;
28     tmp = *n1;
29     *n1 = *n2;
30     *n2 = tmp;
31     return 0;
32 }
33 //這個程式碼有兩個要點:
34 //1,他們的都是靠指標進行返回的,也就是說可以返回多個值
35 //2,在進行邏輯的判斷或其他的操作的時候就把指標轉換成值,如果是函式進行傳值的話就用指標進行傳遞。

 

 

把指標作為函式的返回值

方法:1,定義函式的時候型別定義成指標型別。2,返回值是一個地址。

如下例子:

 

 1 #include <stdio.h>
 2 int main(){
 3     char str[] = "luotianyi";
 4 
 5     char* found(char* str,char ch);
 6     char* p = found(str,'i');
 7     if (p == NULL){
 8         printf("沒有此字元\n");
 9     }
10     else{
11         printf("輸出此字元:%s\n",p);  //輸出字串,因為陣列的名字是陣列的首地址,又因為是地址。所以不需要加上“&”所以也不需要加上“*”。只有字串才可以,字元不行。
12     }
13     return 0;
14 }
15 //我們先定義函式的型別是指標型別
16 char* found(char* str,char ch){     //這裡的括號裡面的char後面要加上“*”是因為我們在宣告和定義的時候沒有說str是一個數組,因此只能通過
17                                     //“*”把傳入的首地址轉換為其元素的值,我們才能在下面對其進行操作。如果把宣告和定義換成:char str []也是可以的
18     int i=0;
19     while (str[i]){
20         if (str[i] == ch){
21             return &str[i];     //把需要的值進行返回的值的地址進行返回。
22         }
23         i++;
24     }
25     return NULL;
26 }

 

 

 

上面的裡面的判斷使用陣列進行的,而我們可以用指標進行判斷。

如下例子:

 1 #include <stdio.h>
 2 int main(){
 3     char str[] = "luotianyi";
 4     char* found(char* str,char ch);
 5     char* p = found(str,'i');
 6     if (p == NULL){
 7         printf("沒有此字元\n");
 8     }
 9     else{
10         printf("輸出此字元:%s\n",p);  //輸出字串,以為陣列名是陣列的首地址。因此不需要加上“&”所以在獲取的時候也不需要加上“*”。只有字串才可以,字元不行。
11     }
12     return 0;
13 }
14 //我們把宣告的型別換成是指標型別
15 char* found(char* str,char ch){     //這裡的括號裡面的char後面要加上“*”是因為我們在宣告和定義的時候沒有說str是一個數組,因此只能通過
16                                     //“*”把傳入的首地址轉換為其元素的值,我們才能在下面對其進行操作。如果把宣告和定義換成:char str []也是可以的
17     while (*str){       //這裡的“*”就是獲取指標的值,其實與str[i]是一樣的
18         if (*str == ch){
19             return str;    //這裡的str在傳入的時候就已經規定是指標的型別了,因此不需要再次*
20         }
21         str++;  //對指標的自加操作
22     }
23     return NULL;
24 }

 

 

 

下面是陣列如何運用指標的

陣列的地址其實是這樣的,例如:int a[2] = {0,1,2};這樣的話其實在記憶體中是對其每一個元素都開闢了一個地址的,假設第一個元素的地址是101,第二個是105,第三個就是這是因為:1,int本身就佔4個位元組,每一個int的元素也是佔4個位元組。2,陣列的地址是具有連貫性的,是一個接著一個的。3,我麼可以通過指標的偏移來取出一個個陣列的內容。而這個的陣列的地址,其實就是第一個元素的地址。下面就讓我們來一個個的證明這些觀點

陣列的地址就是陣列第一個元素的地址

 1 #include <stdio.h>
 2 int main()
 3 {
 4     int sz[] = {1,2,3,4};
 5     printf("%p\n",&sz); //取整個陣列的地址
 6     printf("%p\n",&sz[0]);  //取第一個元素地址
 7     return 0;
 8 }
 9 //輸出結果:
10 //0xffffcc10
11 //0xffffcc10
例子

每一個元素佔4個位元組,陣列的地址具有連貫性

 1 #include <stdio.h>
 2 int main()
 3 {
 4     int sz[] = {1,2,3,4};
 5     printf("%ld\n",&sz[0]);
 6     printf("%ld\n",&sz[1]);
 7     printf("%ld\n",&sz[2]);
 8     printf("%ld\n",&sz[3]);
 9 
10     return 0;
11 }
12 //這裡本來應該是用%p輸出地址的,%p輸出的是16進位制的。但是為了方便看改正了%lp
13 /*
14  * 輸出結果
15  *4294954000
16  *4294954004
17  *4294954008
18  *4294954012
19  */
例子

看看如何通過指標的偏移,進行呼叫

1 #include <stdio.h>
2 int main()
3 {
4     int sz[] = {1,2,3,4};
5     int *p = &sz[0];
6     printf("%d\n",*(p+1));
7     printf("%d\n",*(p+2));
8     return 0;
9 }
例子

 

下面我們來看一個如何用指標運用陣列 

 1 #include <stdio.h>
 2 int main()
 3 {
 4     int sz[] = {1,2,3,4};
 5     int *p;   //把陣列的首字元給了指標p
 6     int num =0;
 7     for (p = &sz[0];p<=&sz[3];p++){  //在這裡我們的迴圈是用地址來定義的,這裡直接定義成&sz也可以,但是有警告
 8         num = num + *p;      //而我們也可以用地址來獲取到對應的值
 9     }
10     printf("num : %d",num);
11     return 0;
12 }
例子

 

 

下面總結下,有三種方法訪問陣列;

1,下標法

1 #include <stdio.h>
2 int main() {
3     int num[] = {0,1,2,3,4,5,6,7,8,9};
4     for(int i=1;i<10;i++){
5         printf("%d\n",num[i]);
6     }
7     return 0;
8 }
例子

2,通過陣列名字獲取到期地址,找出地址的值。

 1 #include <stdio.h>
 2 int main() {
 3     int num[] = {0,1,2,3,4,5,6,7,8,9};
 4     printf("%ld\n",num);    //這裡可以看出,我們直接訪問這個陣列的話是直接訪問到地址的:4294953968為了
 5                                 // 方便我們用長整型的方法輸出
 6     for (int i = 0; i < 10; ++i) {
 7         printf("%d\n",*(num+i));    //因為上面直接訪問了 陣列名字得到的是地址,所以我們直接在陣列前面加上*就可以
 8                                     // 直接找到地址所對應的值
 9                                     //這裡加i實際上因為型別的原因是每次加上四個位元組
10     }
11     return 0;
12 }
例子

3,用指標變數通過指標的偏移而找到值。

1 #include <stdio.h>
2 int main() {
3     int num[] = {0,1,2,3,4,5,6,7,8,9};
4     int *p = &num[0];
5     for(int i=0 ;i<10;i++,p++){
6         printf("%d\n",*p);  
7     }
8     return 0;
9 }
例子

 

陣列的指標偏移1表示指標往右邊移動了一個型別,例如:int的型別加1等於指標地址加上了4個位元組。

指標通過迴圈輸出了陣列所有的元素,然後指標本身就變成了一個野指標。我們可以通過過:野指標-陣列名 =陣列的個數(也可以說是指標的偏移量)

 

函式傳入陣列

陣列做函式引數,說具體是指向陣列的指標變數做函式引數。 
由於陣列名是該陣列的首地址,指標變數的值也是首地址,所以函式的實參和形參都可以指向陣列名或者陣列的指標。於是有了以下四種對應關係:

例子:

 1 #include <stdio.h>
 2 int main()
 3 {
 4     float ave(int *b, float num);   //這裡的是宣告函式
 5     int counter,a[5] = {0};
 6     float all =0, res;
 7 
 8     scanf("%f", &all);
 9     for(counter = 0; counter < all; counter++)
10     {
11         scanf("%d", &a[counter]);
12     }
13     res = ave(a, all);  //這裡把陣列的名字傳入了函式定義的指標變數。和輸入第一次輸入的次數
14     printf("%f", res);
15     return 0;
16 }
17 
18 float ave(int *b, float num)   //這裡和上面宣告函式一樣
19 {
20     int i, sum =0;
21     float ave;
22     for(i = 0; i < num; i++)
23     {
24         sum = sum + b[i];
25     }
26     ave = (float)sum/num;
27     return ave;
28 }

 

這裡傳入函式ave的是陣列a的首地址,而不是將整個陣列傳入。傳入首地址後,ave函式就按照一定順序去訪問a的儲存空間,從而得到a中的資料。

  當然float ave(int *b, float num) 也可以寫為float ave(int b[], float num) 或者float ave(int b[100], float num) 也就是說b[] 後面方括號內可以是任意數字,因為那個數字是沒有意義的,真真起作用的是b和方括號。
總結一下這部分就是:
  陣列做形參,其實就是指標做形參。只要指標向函式內傳入陣列首地址,那麼函式形參和實參是指同一陣列。函式內部對陣列所做的處理,就是對主調函式中的實引數組所作的處理,可以傳回主調函式。

 

指標和字串與函式

f(int arr[ ] ,int n)但是在編譯時是將arr按指標變數處理的,相當於將函式 f 的首部寫成 f ( int* arr,int n)。這兩種寫法是等價的,而%s的輸出可以是字串的地址,而不需要解引用。

輸出字串有三種方法,如下所示。

1 #include <stdio.h>
2 int main(){
3     char ch1[] ="luotianyi";    //第一種是用陣列的方式輸出字串
4     printf("%s\n",ch1);
5     char *ch2 = "luotianyi";    //第二種是利用指標變數儲存字串
6     printf("%s\n",ch2);
7     printf("luotianyi");       //第三種是直接用把字串放到printf中
8     return 0;
9 }

這三種方法有2個區別,第一種利用陣列的方式儲存是把字串儲存在棧中(也就是可以進行修改的地方,也可以進行輸出)。第二種和第三種是儲存在堆中的(也就是隻能輸出,但是不允許進行修改)。

 1 #include <stdio.h>
 2 int main(){
 3     char ch1[] ="luotianyi";
 4     ch1[1] = 'w';   //可以進行修改
 5     printf("%s\n",ch1);
 6     char *ch2 = "luotianyi";
 7     *ch2+1 = 's';    //error: lvalue required as left operand of assignment
 8     printf("%s\n",ch2);
 9     printf("luotianyi");
10     return 0;
11 }

下面我們看看字串是如何傳入函式中的,我們分別演示下用指標變數和陣列變數進行。

 1 #include <stdio.h>
 2 int main(){
 3     char str1[] = "luotianyi";
 4     char* str2 = "my wife";
 5     int strpri(char ch1[],char* ch2) ;
 6     strpri(str1,str2);
 7     return 0;
 8 }
 9 int strpri(char ch1[],char* ch2){   //我們分別用兩種方法傳入
10     char* tmp;  //建立一個指標對其進行轉換
11     tmp = ch1;
12     ch1 = ch2;
13     ch2 = tmp;
14     ch2[3] = 'z';
15     printf("%s\n",ch1);     //在這裡可以看出指標變數所指向的字串內部雖然不可以改變,但是可以對其整體進行其他的操作。
16                             //也就是說字串單個字元的操作是不可以的,但是可以對其整體進行操作
17     printf("%s\n",ch2);         //這裡可以看出我們可以對陣列變數的字串進行操作
18     return 0;
19 }
例子

堆與棧的區別還有就是棧可以儲存多份一樣的值,而堆只儲存一份,其餘的也要呼叫的話就是多個變數都是指向堆的這一份。

 1 #include <stdio.h>
 2 int main(){
 3     char* a = "luotianyi";
 4     char* b = "luotianyi";
 5     printf("%ld\n",a);
 6     printf("%ld\n",b);
 7     return 0;
 8 }
 9 /*
10  * 4299173896
11  * 4299173896
12  * */
堆的示例

 

指標字串 or 陣列字串  

 1 #include <stdio.h>
 2 int main(){
 3     char* ch[] = {"luotianyi","wsj","woaini"};    //這就是建立一個數組字串或是叫做指標字串
 4     for (int i = 0; i < 3; ++i) {
 5         printf("%s\n",ch[i]);   //輸出全部
 6     }
 7     printf("%c\n",ch[1][2]);    //第一個下標是第幾排,第二個是低階列
 8     printf("%c\n",*(*(ch+1)+1));  //外面的數字是第幾橫排,裡面的是橫排的第幾個數字
 9     return 0;
10 }

 


---------------------
部分來源和程式碼出自以下連結
來源:CSDN
原文:https://blog.csdn.net/C2681595858/article/details/53750577
版權宣告:本文為博主原創文章,轉載請附上博文連結!