[例6-11]用指針法輸入輸出二維數組各元素。 #include<stdio.h> main() { int a[3][4],*ptr; int i,j; ptr=a[0]; for(i=0;i<3;i++) for(j=0;j<4;j++) scanf("%d",ptr++);/*指針的表示方法*/ ptr=a[0]; for(i=0;i<3;i++) { for(j=0;j<4;j++) printf("%4d",*ptr++); printf("/n"); } } 運行程序: RUN 1 2 3 4 5 6 7 8 9 10 11 12 1 2 3 4 6.4.3 數組指針作函數的參數 學習了指向一維和二維數組指針變量的定義和正確引用后,我們現在學習用指針變量作 函數的參數。 [例6-12] 調用子程序,實現求解一維數組中的最大元素。 我們首先假設一維數組中下標為0的元素是最大和用指針變量指向該元素。后續元素與該 元素一一比較,若找到更大的元素,就替換。子程序的形式參數為一維數組,實際參數是指 向一維數組的指針。 # include <stdio.h> m a i n ( ) { int sub_max(); / * 函數聲明* / int n,a[10],*ptr=a; / *定義變量,并使指針指向數組* / int max; f o r ( n = 0 ; n < = i - 1 ; n + + ) / *輸入數據* / s c a n f ( " % d " , & a [ n ] ) ; m a x = s u b _ m a x ( p t r , 1 0 ) ; / * 函數調用,其實參是指針* / p r i n t f ( " m a x = % d / n " , m a x ) ; } int sub_max(b,i) / * 函數定義,其形參為數組* / int b[],i; { int temp,j; t e m p = b [ 0 ] ; f o r ( j = 1 ; j < = 9 ; j + + ) if(temp<b[j]) temp=b[j]; return temp; } 程序的m a i n ( )函數部分,定義數組a 共有1 0個元素,由于將其首地址傳給了p t r,則指針 變量ptr 就指向了數組,調用子程序,再將此地址傳遞給子程序的形式參數b,這樣一來,b 數組在內存與a 數組具有相同地址,即在內存完全重合。在子程序中對數組b 的操作,與操 作數組a 意義相同。其內存中虛實結合的示意如圖6 - 9所示。 m a i n ( )函數完成數據的輸入,調用子程序并輸出運行結果。s u b _ m a x ( )函數完成對數組元 素找最大的過程。在子程序內數組元素的表示采用下標法。運行程序: R U N 1 3 5 7 9 2 4 6 8 0 m a x = 9 [例6-13] 上述程序也可采用指針變量作子程序的形式參數。 # include <stdio.h> m a i n ( ) { int sub_max(); int n,a[10],*ptr=a; int max; f o r ( n = 0 ; n < = 9 ; n + + ) s c a n f ( " % d " , & a [ n ] ) ; m a x = s u b _ m a x ( p t r , 1 0 ) ; p r i n t f ( " m a x = % d / n " , m a x ) ; } int sub_max(b,i) / *形式參數為指針變量* / int *b,i; { int temp,j; t e m p = b [ 0 ] ; / *數組元素指針的下標法表示* / f o r ( j = 1 ; j < = i - 1 ; j + + ) if(temp<b[j]) temp=b[j]; return temp; } 在子程序中,形式參數是指針,調用程序的實際參數p t r為指向一維數組a的指針,虛實結 合,子程序的形式參數b得到p t r的值,指向了內存的一維數組。數組元素采用下標法表示,即 一維數組的頭指針為b,數組元素可以用b [ j ]表示。其內存中虛實參數的結合如圖6 - 1 0所示。 運行程序: R U N 1 3 5 7 9 2 4 6 8 0¿ m a x = 9 [例6-14] 上述程序的子程序中,數組元素還可以用指針表示。 # include <stdio.h> m a i n ( ) { int sub_max(); int n,a[10],*ptr=a; int max; f o r ( n = 0 ; n < = 9 ; n + + ) s c a n f ( " % d " , & a [ n ] ) ; m a x = s u b _ m a x ( p t r , 1 0 ) ; p r i n t f ( " m a x = % d / n " , m a x ) ; } int sub_max(b,i)/ *子程序定義* / int *b,i; { int temp,j; t e m p = * b + + ; f o r ( j = 1 ; j < = i - 1 ; j + + ) if(temp<*b) temp=*b++; return temp; } 在程序中,賦值語句t e m p = * b + +;可以分解為:t e m p = * b;b + +;兩句,先作t e m p = * b;后 作b + +;程序的運行結果與上述完全相同。 對上面的程序作修改,在子程序中不僅找最大元素,同時還要將元素的下標記錄下來。 # include <stdio.h> m a i n ( ) { int *max();/* 函數聲明* / int n,a[10],*s,i; f o r ( i = 0 ; i < 1 0 ; i + + ) / * 輸入數據* / scanf("%d",a+i); s = m a x ( a , 1 0 ) ; / *函數調用* / p r i n t f ( " m a x = % d , i n d e x = % d / n " , * s , s - a ) ; } int *max(a,n) / *定義返回指針的函數* / int *a,n; { int *p,*t; / * p 用于跟蹤數組,t用于記錄最大值元素的地址* / f o r ( p = a , t = a ; p - a < n ; p + + ) if(*p>*t) t=p; return t; } 在m a x()函數中,用p - a < n來控制循環結束, a是數組首地址, p用于跟蹤數組元素的地址,p - a正好是所跟蹤元素相對數組頭的距離,或者說是所跟蹤元素相對數組頭的元素個數,所以在m a i n ( )中,最大元素的下標就是該元素的地址與數組頭的差,即s - a。運行程序: R U N 1 3 5 7 9 2 4 6 8 0¿ m a x = 9 , i n d e x = 4 [例6-15] 用指向數組的指針變量實現一維數組的由小到大的冒泡排序。編寫三個函數用于輸入數據、數據排序、數據輸出。 在第5章的例題中,我們介紹過選擇法排序及算法,此例再介紹冒泡排序算法。為了將一組n個無序的數整理成由小到大的順序,將其放入一維數組a [ 0 ]、a [ 1 ]. . .a [ n - 1 ]。冒泡算法如下: (開序) ① 相鄰的數組元素依次進行兩兩比較,即a [ 0 ]與a [ 1 ]比、a [ 1 ]與a [ 2 ]比. . . a [ n - 2 ]與a [ n - 1 ]比,通過交換保證數組的相鄰兩個元素前者小,后者大。此次完全的兩兩比較,能免實現a [ n - 1 ]成為數組中最大。 ② 余下n - 1個元素,按照上述原則進行完全兩兩比較,使a [ n - 2 ]成為余下n - 1個元素中最大。 ③ 進行共計n - 1趟完全的兩兩比較,使全部數據整理有序。 下面給出一趟排序的處理過程: 4個元素進行3次兩兩比較,得到一個最大元素。若相鄰元素表示為a [ j ]和a [ j + 1 ],用指針 變量P指向數組,則相鄰元素表示為* ( P + j )和* ( P + j + 1 )程序實現如下: # include<stdio.h> #define N 10 m a i n ( ) { void input(); / *函數聲明* / void sort(); void output(); int a[N],*p; / *定義一維數組和指針變量* / i n p u t ( a , N ) ; / *數據輸入函數調用,實參a是數組名* / p = a ; / *指針變量指向數組的首地址* / s o r t ( p , N ) ; / *排序,實參p是指針變量* / o u t p u t ( p , N ) ; / *輸出,實參p是指針變量* / } void input(arr,n) / *無需返回值的輸入數據函數定義,形參a r r 是數組* / int arr[],n; { int i; printf("input data:/n"); for ( i = 0 ; i < n ; i + + ) / *采用傳統的下標法*/ s c a n f ( " % d " , & a r r [ i ] ) ; } void sort(ptr,n) / *冒泡排序,形參ptr 是指針變量* / int *ptr,n; { int i,j,t; for ( i = 0 ; i < n - 1 ; i + + ) for ( j = 0 ; j < n - 1 - i ; j + + ) if (*(ptr+j)>*(ptr+j+1))/相*臨兩個元素進行比較*/ { t = * ( ptr + j ) ; / *兩個元素進行交換* / * ( ptr + j ) = * ( ptr + j + 1 ) ; * ( ptr + j + 1 ) = t ; } } void output(arr,n) / *數據輸出* / int arr[],n; { int i,*ptr=arr; / *利用指針指向數組的首地址* / printf("output data:/n"); for ( ; ptr - a r r < n ; ptr + + ) / *輸出數組的n個元素* / printf ( " % 4 d " , * ptr ) ; printf ( " / n " ) ; }
由于C程序的函數調用是采用傳值調用,即實際參數與形式參數相結合時,實參將值傳給形式參數,所以當我們利用函數來處理數組時,假如需要對數組在子程序中修改,只能傳遞數組的地址,進行傳地址的調用,在內存相同的地址區間進行數據的修改。在實際的應用中, 假如需要利用子程序對數組進行處理,函數的調用利用指向數組(一維或多維)的指針作參數,無論是實參還是形參共有下面四種情況: 我們知道,二維數組在內存中是按行存放,假定我們定義二維數組和指針如下: int a[3][4],* p = a [ 0 ] ; 則指針p就指向二維數組。其在內存的存放情況如圖6 - 11所示。 從上述存放情況來看,若把二維數組的首地址傳遞給指針p,則映射過程如圖6 - 11 所示。我們只要找到用p所表示的一維數組中最大的元素及下標,就可轉換為在二維數組中的 行列數。 # include<stdio.h> m a i n ( ) { int a[3][4],*ptr,i,j,max,maxi,maxj; / * m a x 是數組的最大, m a x i 是最大元素所在行, m a x j 是最大元素所在列* / f o r ( i = 0 ; i < 3 ; i + + ) f o r ( j = 0 ; j < 4 ; j + + ) s c a n f ( " % d " , & a [ i ] [ j ] ) ; p t r = a [ 0 ] ; / * 將二維數組的首地址傳遞給指針變量* / m a x _ a r r ( p t r , & m a x , & m a x i , 1 2 ) ; m a x j = m a x i % 4 ; / * 每行有四個元素,求該元素所在列* / m a x i = m a x i / 4 ; / * 求該元素所在行* / printf("max=%d,maxi=%d,maxj=%d",max,maxi,maxj); } int max_arr(b,p1,p2,n) int *b,*p1,*p2,n; / * b 指向二維數組的指針, p 1指向最大值,p 2 指向最大值在一維數組中的位置, * / / * n 是數組的大小* / { int i; *p1=b[0]; *p1=0; f o r ( i = 1 ; i < n ; i + + ) / * 找最大* / if (b[i]>*p1) {*p1=b[i]; *p2=i;} } 運行程序: 6.4.4 指針與字符數組 在前面的課程中,我們用過了字符數組,即通過數組名來表示字符串,數組名就是數組的首地址,是字符串的起始地址。下面的例子用于簡單字符串的輸入和輸出。 #include<stdio.h> main() { char str[20]; gets(str); printf("%s/n",str); } 現在,我們將字符數組的名賦予一個指向字符類型的指針變量,讓字符類型指針指向字 符串在內存的首地址,對字符串的表示就可以用指針實現。其定義的方法為:charstr[20], *P=str;這樣一來,字符串str就可以用指針變量P來表示了。 #include<stdio.h> main() { char str[20],*p=str;/*p=str則表示將字符數組的首地址傳遞給指針變量p*/ gets(str); printf("%s/n",p); } RUN good morning! goodmorning! 需要說明的是,字符數組與字符串是有區別的,字符串是字符數組的一種非凡形式,存儲時以“/0”結束,所以,存放字符串的字符數組其長度應比字符串大1。對于存放字符的字符數組,若未加“/0”結束標志,只能按逐個字符輸入輸出。 [例6-18]字符數組的正確使用方法。 #include<stdio.h> main() { charstr[10],*p=str; int i; scanf("%s",str);/*輸入的字符串長度超過10*/ for(i=0;i<10;i++) printf("%c",*p++);/*正確輸出*/ printf("/n"); p=str; printf("%s",p);/*字符數組無'/0'標志,輸出出錯*/ puts(str);/*字符數組無'/0'標志,輸出出錯*/ } 對上述程序中字符數組以字符串形式輸出,若無“/0”標志,則找不到結束標志,輸出出 錯。 [例6-19]用指向字符串的指針變量處理兩個字符串的復制。 字符串的復制要注重的是:若將串1復制到串2,一定要保證串2的長度大于或等于串1。 #include<stdio.h> main() { char str1[30],str2[20],*ptr1=str1,*ptr2=str2; printf("inputstr1:"); gets(str1);/*輸入str1*/ printf("inputstr2:"); gets(str2);/*輸入str2*/ printf("str1------------str2/n"); printf("%s.......%s/n",ptr1,ptr2); while(*ptr2)*ptr1++=*ptr2++;/*字符串復制*/ *ptr1='/0';/*寫入串的結束標志*/ printf("str1------------str2/n"); printf("%s.......%s/n",str1,str2); } 在程序的說明部分,定義的字符指針指向字符串。語句while(*ptr2)*ptr1++=*ptr2++;先測試表達式的值,若指針指向的字符是“/0”,該字符的ASCII碼值為0,表達式的值為假,循環結束,表達式的值非零,則執行循環*ptr1++=*ptr2++。語句*ptr1++按照運算優先級別,先算*ptr1,再算ptr1++。 現在,我們修改程序中語句printf("%s.......%s/n",str1,str2)為printf("%s.......%s/n",ptr1,ptr2); 會出現什么結果呢?請思考。 [例6-20]用指向字符串的指針變量處理兩個字符串的合并。 #include<stdio.h> main() { char str1[50],str2[20],*ptr1=str1,*ptr2=str2; printf("inputstr1:"); gets(str1); printf("inputstr2:"); gets(str2); printf("str1------------str2/n"); printf("%s.......%s/n",ptr1,ptr2); while(*ptr1)ptr1++;/*移動指針到串尾*/ while(*ptr2)*ptr1++=*ptr2++;串/*連接*/ *ptr1='/0';/*寫入串的結束標志*/ ptr1=str1;ptr2=str2; printf("str1------------------str2/n"); printf("%s.......%s/n",ptr1,ptr2); } 需要注重的是,串復制時,串1的長度應大于等于串2;串連接時,串1的長度應大于等于串1與串2的長度之和。