第一篇已經討論了很多典型錯誤,可以供學習參考。本節主要列舉一些典型問題。這裡沒有用錯誤一詞,而改用問題,就是說設計的語句本身沒有錯誤,但沒有達到要求,或者雖然達到要求,但存在效率問題。
16.3.1 沒有涵蓋全部條件
有時沒有仔細審題,漏掉了控制程序執行的條件。請看下面的例子。
【例16.4】這個程序對輸出的數據進行適當運算之後,如果a<b,則交換它們的值,然後輸出兩個數的關係。請找出該程序的問題。
#include <stdio.h> int main ( ) { int a=0 ,b=0 ; printf (" 輸入兩個整數:" ,a ,b ); scanf ("%d%d" ,&a ,&b ); a= (a= (5*a ,3*a ),a+9 ); if (a<b ) { a+=b ;b=a-b ;a-=b ; } printf ("%d>%d\n" ,a ,b ); return 0 ; }
這個程序只是判斷「a<b」的情況,忽視了「a=b」的情況,運行時會產生如下結果:
輸入兩個整數:0 0 9>9
可能會有人問為何輸出語句沒有區分「a>b」和「a<b」的情況,其實在「a<b」的情況裡,已經調整為「a>b」,所以只需一個輸出語句。
在對a和b進行運算之後,先增加如下判定條件:
if (a==b ) printf ("%d=%d\n" ,a ,b );
運行結果為:
輸入兩個整數: 0 9 9=9 9>9
由此可見,第2個的輸出語句變成兩者的公用語句。可能有人認為只要簡單地將這條輸出語句限制在「a<b」的復合語句之中執行即可,即:
if (a<b ) { a+=b ;b=a-b ;a-=b ; printf ("%d>%d\n" ,a ,b ); }
這是行不通的,因為破壞了原來的正確路徑,當「a>b」,不需要執行第2個判斷語句時,就沒有相應的輸出語句。即當輸入「2-2」時,就不執行輸出語句,造成錯誤。
為了不影響原來的輸出,應該在輸出「a=b」之後,直接結束程序的運行。下面就是修改後的正確程序。
#include <stdio.h> int main ( ) { int a=0 ,b=0 ; printf (" 輸入兩個整數:" ,a ,b ); scanf ("%d%d" ,&a ,&b ); a= (a= (5*a ,3*a ),a+9 ); if (b>a ) b-=a ; else b+=a ; if (a==b ) { printf ("%d=%d\n" ,a ,b ); return 0 ; } if (a<b ) { a+=b ;b=a-b ;a-=b ; } printf ("%d>%d\n" ,a ,b ); return 0 ; }
三種情況的運行示範如下。
輸入兩個整數: 2 -2 15>13 輸入兩個整數: 2 0 15=15 輸入兩個整數: 5 9 33>24
需要注意的是,不能將程序的第2個分支部分修改為如下形式:
if (a<b ) printf ("%d>%d\n" ,b ,a ); else printf ("%d>%d\n" ,a ,b );
這樣的修改雖然保證了輸出結果正確,但不符合原程序的要求,即交換a和b的值。
【例16.5】下面是一個求複數除法的程序。
typedef struct { double re , im ; }complex ; complex p (complex x , complex y ) { double d ; complex z ; d = y.re*y.re + y.im*y.im ; if (d==0 ) return z ; z.re = (x.re * y.re + x.im*y.im )/d ; z.im = (x.im * y.re - x.re*y.im )/d ; return ( z ); } #include <stdio.h> void main ( ) { complex a ,b ,c ; a.im=0 ; a.re=1 ; b.im=1.0 ; b.re=1.0 ; c=p (a ,b ); printf ("%lf + %lfi\n" ,c.re ,c.im ); }
這個程序的輸出為:0.500000+-0.500000i
要求程序的輸出為:0.500000-0.500000i
修改程序的設計,使它滿足需要。這個程序能處理除數為零的情況嗎?
【解答】要解決這個問題,可以簡單地使用判斷語句判斷虛部的符號位,例如:
if (c.im >= 0 ) printf ("%lf + %lfi\n" , c.re , c.im ); else printf ("lf - %lfi\n" , c.re , -c.im );
也可以將符號存入一個字符型的變量中,作為符號位。假設符號位為flag,當符號為正時,flag的值為'+',符號為負時,其值為'\0'。這時就可以使用統一的輸出語句
printf ("%lf%c%lfi\n" , c.re , flag , c.im );
不過,它的輸出格式沒有前者靈活。
在主程序中,沒有區分除數為零的情況,所以這個程序不能處理除數為零的情況。
在除法程序中,當除數為零,執行語句
if (d == 0 ) return z ;
時,z是沒有被初始化的,主程序的輸出語句將輸出隨機數字。可能有的人認為可以使用語句
return 1 ;
來解決這個問題。其實,這樣也是不行的。因為p返回結構類型的函數,返回1變成返回整數值,與原來的類型不符,編譯就會報錯。對錯誤處理是使用exit函數,即
exit (1 );
如果使用exit函數,需要增加頭文件並修改函數名,這裡暫不使用這種方法。
在p函數里將z初始化為0,當除數為0時,給出除數為零的信息並設置z.re為-1,通過語句
return z ;
直接退出函數,即改為
if (d==0 ) { printf (" 除數為零,結束運行。\n" ); z.re=-1 ; return z ; }
因為只是退出p函數,所以主程序裡還需要處理除數為零的信息。這可以用p函數里面為z設置的信息來處理,即
if (c.re == -1 ) return 0 ;
完整的程序如下。
#include <stdio.h> typedef struct { double re , im ; }complex ; complex p (complex x , complex y ) { double d ; complex z ; z.re=0 ; z.im=0 ; d = y.re*y.re + y.im*y.im ; if (d==0 ) { printf (" 除數為零,結束運行。\n" ); z.re=-1 ; return z ; } z.re = (x.re * y.re + x.im*y.im )/d ; z.im = (x.im * y.re - x.re*y.im )/d ; return ( z ); } int main ( ) { complex a ,b ,c ; a.im=0 ; a.re=1 ; b.im=1.0 ; b.re=1.0 ; c=p (a ,b ); if (c.re==-1 ) return 0 ; if (c.im>=0 ) printf ("%lf + %lfi\n" ,c.re ,c.im ); else printf ("%lf - %lfi\n" ,c.re , -c.im ); return 0 ; }
如果使用exit函數,就可以直接在函數里結束程序運行,免去在主函數里還要判別的重複動作。不過,這時不能再使用p函數名,p的名字在stdlib.h中已經有定義,所以把函數名改為pe。使用字符變量flag存儲符號。
完整的程序如下。
#include <stdlib.h> #include <stdio.h> typedef struct { double re , im ; }complex ; complex pe (complex x , complex y ) { double d ; complex z ; d = y.re*y.re + y.im*y.im ; if (d==0 ) { printf (" 被除數為零,結束運行。\n" ); exit (1 ); } z.re = (x.re * y.re + x.im*y.im )/d ; z.im = (x.im * y.re - x.re*y.im )/d ; return ( z ); } int main ( ) { complex a ,b ,c ; char flag='\0' ; printf (" 輸入第1 個複數:" ); scanf ("%lf%lf" ,&a.re ,&a.im ); printf (" 輸入第2 個複數:" ); scanf ("%lf%lf" ,&b.re ,&b.im ); c=pe (a ,b ); if (c.im>=0 ) { flag='+' ; printf ("%lf%c%lfi\n" ,c.re ,flag ,c.im ); } else printf ("%lf%c%lfi\n" ,c.re ,flag ,c.im ); return 0 ; }
程序示範運行後的輸出結果如下。
輸入第1 個複數: 1 0 輸入第2 個複數: 1 1 0.500000-0.500000i 輸入第1 個複數: 4 3 輸入第2 個複數: 2 1 2.200000+0.400000i 輸入第1 個複數: 5 9 輸入第2 個複數: 0 0 除數為零,結束運行。
其實,如果是自己編寫程序,一般都要自力更生,不要依靠被調用的程序。也就是在自己組織輸入數據時,應該剔除不合理的數據。
16.3.2 條件超出範圍
這種情況是指設計的條件過寬,超出範圍而造成程序運行結果不正確。
【例16.6】這是計算具有從1開始的10個自然數的數組前5項之和的程序,要求將計算結果用如下形式輸出。
1+2+3+4+5=15
下面是它的源程序。
#include <stdio.h> int main ( ) { int a[ ]={1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10} ; int i=0 , sum=0 ; for (i=0 ;i<5 ;i++ ){ sum=sum+a[i] ; printf ("%d+" ,a[i] ); } printf ("=%d\n" ,sum ); }
這個程序的實際輸出為:
1+2+3+4+5+=15
由輸出結果可見,比要求多輸出一個「+」號。一種方法是在for循環體內解決,例如將1條輸出語句改為if~else語句實現。
if (i != 4 ) printf ("%d+" ,a[i] ); else printf ("%d" ,a[i] );
另一種方法是按計算順序解決,把最後一次的計算剝離出去單獨處理,這就不需要判斷語句了。完整的程序如下。
#include <stdio.h> int main ( ) { int a[ ]={1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10} ; int i=0 , sum=0 ; for (i=0 ;i<4 ;i++ ){ sum=sum+a[i] ; printf ("%d+" ,a[i] ); } printf ("%d=%d\n" ,a[i] ,sum+a[i] ); return 0 ; }
【例16.7】下面的程序用來求1+2+3+…+n≤10000時的最大的n值。
#include <stdio.h> int main ( ) { int sum , i ; sum=0 ; i=0 ; while (sum<=10000 ) { ++i ; sum+=i ; } printf ("n=%d\n" ,i ); return 0 ; }
程序輸出為:
n=141
這個計算結果並不正確,因為語句
sum<=10000
判定的條件是必要條件。計算肯定要使條件滿足才能停止循環。符合條件時的i值已經超過1次,所以應該減去1次,即140次。
可能有人以為使用do~while不用減1,其實是一樣的,因為循環的條件一樣。後者雖然先計算後判別條件,但不滿足大於10000時,它會繼續循環。一旦滿足,當然就已經多計算一次了。下面是do~while的演示程序。
#include <stdio.h> int main ( ) { int sum=0 ; int i=0 ; do{ ++i ; sum+=i ; }while (sum<=10000 ); // 循環結束時,i 會多加1 而sum 會多加i printf ("1+2+3+ …+%d=%d\n" ,i-1 , sum-i ); // 減去多記的部分 return 0 ; }
運行結果如下:
1+2+3+ …+140=9870
其實,while和do~while還是有細微區別的,稍不注意也會使輸出結果不符合要求。請看下面兩個例子的比較。
【例16.8】計算兩個數字之差,直到輸入數字為0時停止計算。
#include <stdio.h> int main ( ) { int a ,b ,x ; printf (" 輸入兩個數字:" ); do { scanf ( "%d %d" ,&a ,&b ); x=a-b ; printf ( "x=%d\n" , x ); printf (" 輸入兩個數字:" ); } while ( a !=0&&b !=0 ); printf (" 退出程序!\n" ); return 0 ; }
運行結果如下:
輸入兩個數字: 6 89 x=-83 輸入兩個數字: 0 8 x=-8 輸入兩個數字:退出程序!
顯然,這個程序輸出了不需要的信息。問題在於它先執行運算後判斷條件。可以推知,這個程序開始執行時,如運行實例所示,當輸入數字中有一個為0,則輸出結果就含多餘信息。
應該先判斷再執行,修改的程序如下。
#include <stdio.h> int main ( ) { int a ,b ,x ; printf (" 輸入兩個數字:" ); scanf ( "%d %d" , &a ,&b ); while ( a !=0 && b !=0 ) // 任意一個為0 則退出 { x=a-b ; printf ( "x=%d\n" , x ); printf (" 輸入兩個數字:" ); scanf ( "%d %d" , &a ,&b ); } printf (" 退出程序!\n" ); return 0 ; }
運行示範如下:
輸入兩個數字:0 9 退出程序! 輸入兩個數字:5 68 x=-63 輸入兩個數字:89 3 x=86 輸入兩個數字:8 0 退出程序!
16.3.3 減少循環次數
這種情況是指設計的程序運行結果正確,但應該改進以提高效率。一般是針對循環控制而言,即應減少循環的次數以提高程序的效率。這裡舉幾個典型的例子進行比較說明。
1.尋找逃犯
【例16.9】一輛汽車撞人後逃跑,4個目擊者提供如下線索:
甲:牌照三、四位相同;
乙:牌號為31xxxx;
丙:牌照五、六位相同;
丁:三~六位是一個整數的平方。
為了從這些線索中求出牌照號碼,只要求出後四位再加上310000即可。這個四位數又是前兩位相同,後兩位也相同,互相又不相同並且是某個整數的平方。利用計算機計算速度快的特點,把所有可能的方式都試一下,從中找出符合條件的數。這就是所謂的窮舉法。參考程序如下:
#include <stdio.h> void main ( ) { int i ,j ,k ,c ; for (i=1 ; i<=9 ; i++ ) for (j=0 ; j<=9 ; j++ ) if (i !=j ) { k=i*1000+i*100+j*10+j ; for (c=1 ; c*c<k ; c++ ); if (c*c==k ) printf (" 牌照號碼是: %ld 。\n" ,310000+k ); } }
運行輸出如下:
牌照號碼是:317744
因為後面4位數,1000的平方根大於31,所以窮舉實驗時,變量c不需從1開始,而可以從31開始尋找一個整數的平方。為了提高效率,for語句可以改為如下形式:
for (c=31 ; c*c<k ; c++ );
2.百錢買百雞問題
【例16.10】設每隻母雞值3元,每隻公雞值2元,兩隻小雞值1元。現要用100元錢買100隻雞,問能同時買到母雞、公雞、小雞各多少隻?
如果要求程序在找到解的同時,輸出循環的次數。希望尋找一個循環次數較少的算法。
設母雞、公雞、小雞分別為i、j、k只,則可以列出如下兩個方程:
這裡有3個未知數,所以是一個不定方程。要求同時買到母雞、公雞、小雞,也就是給出一個限制條件:任何一個不能為0。這需要使用三重循環,通過枚舉找出所有符合條件的解答。
小雞需要從2開始,每次增加2。由於已經考慮讓i和j從1開始枚舉,所以不需要判別如下附加條件:
i*j*k !=0 // 參考程序 #include <stdio.h> int main ( ) { int m=0 ,n=0 ,sum=0 ; int i ,j ,k ; for (i=1 ;i<100 ;i++ ) { ++sum ; for (j=1 ; j<100 ;j++ ) { ++sum ; for (k=2 ;k<100 ;k=k+2 ) { ++sum ; m=i+j+k ; n=3*i+2*j+k/2 ; if ((m==100 )&& (n==100 )) { printf (" 母雞:%2d 公雞:%2d 小雞:%2d\n" ,i ,j ,k ); } } } } printf (" 一共循環%d 次。\n" ,sum ); return 0 ; }
程序運行結果如下:
母雞: 2 公雞:30 小雞:68 母雞: 5 公雞:25 小雞:70 母雞: 8 公雞:20 小雞:72 母雞:11 公雞:15 小雞:74 母雞:14 公雞:10 小雞:76 母雞:17 公雞: 5 小雞:78 一共循環490149 次。
其實,第3層就循環了480249次。
考慮到母雞為3元一隻,100元都買母雞,最多也只能買33只。要求每個品種都要,小雞隻能為偶數,因此最多為30只,即第一循環變量i可從1到30。
公雞為2元一隻,最多能買50只。因為至少需要1只母雞和2只小雞,所以公雞不會超出50-3=47(只)。因循環時已經決定枚舉的母雞數i,一隻母雞相當1.5只公雞,所以第二層循環時,公雞j只要從1到47-1.5i即可。
因為i+j+k=100,所以直接求得k=100-i-j,不再需要第3層循環,即:
k=100-i-j ; if (3*i+2*j+0.5*k==100 ) printf (" 母雞:%2d 公雞:%2d 小雞:%2d\n" , i , j , k ); // 改進的算法 #include <stdio.h> int main ( ) { int k=0 ,sum=0 ; int i ,j ; for (i=1 ;i<=30 ;i++ ) { ++sum ; for ( j=1 ; j<=47-1.5*i ;j++ ) { ++sum ; k=100-i-j ; if (3*i+2*j+0.5*k==100 ) { printf (" 母雞:%2d 公雞:%2d 小雞:%2d\n" ,i ,j ,k ); } } } printf (" 一共循環%d 次。\n" ,sum ); return 0 ; }
程序運行結果如下:
母雞: 2 公雞:30 小雞:68 母雞: 5 公雞:25 小雞:70 母雞: 8 公雞:20 小雞:72 母雞:11 公雞:15 小雞:74 母雞:14 公雞:10 小雞:76 母雞:17 公雞: 5 小雞:78 一共循環735 次。
其中第二層循環705次。
3.雞兔同籠
【例16.11】大約在1500年前,《孫子算經》中記載了一個有趣的問題。書中是這樣敘述的:「今有雞兔同籠,上有三十五頭,下有九十四足,問雞兔各幾何?」
解答思路是這樣的:假如砍去每隻雞、每隻兔一半的腳,則每隻雞就變成了「獨角雞」,每隻兔就變成了「雙腳兔」。由此可知:
(1)雞和兔的腳的總數就由94只變成了47只;
(2)如果籠子裡有一隻兔子,則腳的總數就比頭的總數多1。因此,腳的總只數47與總頭數35的差,就是兔子的只數,即47-35=12(只)。
(3)知道兔子的只數,則雞的只數為:35-12=23(只)。
這一思路新穎而奇特,其「砍足法」也令古今中外數學家讚歎不已。這種思維方法叫化歸法。化歸法就是在解決問題時,先不對問題採取直接的分析,而是將題中的條件或問題進行變形,使之轉化,直到最終把它歸成某個已經解決的問題。
下面使用計算機來求解雞兔同籠問題。
設雞為i只,兔為j只,則有:
使用i和j分別表示兩層循環,逐次枚舉試驗,當滿足上述條件時,就可求出雞有i只,兔有j只。下面是按此思想編寫的程序,sum表示執行循環的總次數。
// 雞兔同籠 #include <stdio.h> int main () { int sum=0 ; int i ,j ; for ( i=1 ;i<35 ; i++ ) { sum++ ; for ( j=1 ;j<35 ;j++ ) { sum++ ; if ((i+j==35 )&& (2*i+4*j==94 )) printf (" 雞有%d 只,兔有%d 只。\n" ,i ,j ); } } printf (" 一共循環%d 次。\n" ,sum ); return 0 ; }
程序運行結果如下:
雞有23 只,兔有12 只。 一共循環1190 次。
其實,第二個循環執行1156次。由此可見,這個循環次數很大,所以應該減少第二個循環的次數。如果將它改為「j=35-i」,則會降為595次。
通過分析雞兔關係,可以改進程序的效率。
(1)兩隻雞和一隻兔子的腳數相等,所以雞頭的數量不會超過三分之二,即i<25,j<13。
(2)給定一個i,j的初始值應該是35-i。
// 改進的算法 #include <stdio.h> int main () { int sum=0 ; int i ,j ; for ( i=1 ;i<24 ; i++ ) { sum++ ; for ( j=35-i ;j<13 ;j++ ) { sum++ ; if ((i+j==35 )&& (2*i+4*j==94 )) printf (" 雞有%d 只,兔有%d 只。\n" ,i ,j ); } } printf (" 一共循環%d 次。\n" ,sum ); }
程序運行結果如下:
雞有23 只,兔有12 只。 一共循環24 次。
其實,要等到j=35-i<13時,才進入第二個循環,而且僅執行1次。
由這兩個例子可見,循環次數的控制很重要。
5.複製字符串
【例16.12】下面是一個複製字符串的程序,找出提高效率的解決方法。
#include <stdlib.h> #include <stdio.h> char *mycopy (char *dest ,char *src ) { if (dest == NULL || src == NULL ) return dest ; while (*dest++=*src++ ); return dest ; } void main ( ) { char s2[16]="how are you ?" , s1[16]=" " ; mycopy (s1 ,s2 ); printf (s1 ); // 輸出how are you ? printf ("\n" ); }
【分析】程序主要的開銷是while語句。在語句
while (*dest++=*src++ );
中,首先要對src指針變量進行讀操作,讀出src所指向的地址,再對這個地址進行讀操作。同樣,對dest指針變量也要進行類似操作,讀出dest所指向的地址,再對這個地址進行寫操作。即對變量本身有兩次讀操作,根據對變量所指向的地址,進行讀寫操作,還要分別執行「++」操作,總共進行6次操作。
由於它分別對dest和src進行3次操作,造成效率降低。假設地址相差len,即有
int len=dest-src ; while (* (src+len )=*src++ );
這就把對目標地址dest的訪問,變成對源地址src訪問的一個增量len,則以後只要讀一次內存,再加上這個源地址的增量len,就可以代替對目的地址的訪問。這就將6次操作降為4次,提高了效率。
// 提高效率的程序 #include <stdlib.h> #include <stdio.h> char *mycopy (char *dest ,char *src ) { int len=dest-src ; if (dest == NULL || src == NULL ) return dest ; while (* (src+len )=*src++ ); return dest ; } void main ( ) { char s2[16]="how are you ?" , s1[16]=" " ; mycopy (s1 ,s2 ); printf (s1 ); printf ("\n" ); }
但是這個辦法仍然是1個1個字節地複製字符串。內存是按32位,4個字節存儲數據的,使用整數指針,就可以按4字節訪問字符串。
【例16.13】演示按4個字節賦值字符串的例子。
#include <stdio.h> void main ( ) { char s2[32]="0123456789ABCDEF12" ; char s1[32]=" " ; int *p , *p2 , i=0 ; p= (int* )s2 ; p2= (int* )s1 ; *p2=*p ; for (i=0 ;i<5 ;i++ ,*p2=* (p+i )) printf ("%s ,%x ,%x\n" ,(char* )p2 ,*p2 ,* (p+i )); }
這個程序只是演示使用整數指針p2每次從字符串中得到4個字符的過程。注意要將字符類型指針強制轉換為整數指針,第1次使用「*p2=*p;」使得*p2獲得p指向地址裡第1個4字節字符。在for語句中使用「*(p+i)」讀取下一個4字節內容。第5次只有2個字節(字符1和2)內容,但它還有一個結束符『\0』,所以實際是3個字節的有效內容。
從下面給出程序輸出結果可知,輸出分為3列,左邊是賦值給*p2的4個字符,中間是這4個字符所對應的ASCII編碼。右邊是*(p+i)的內容,也用ASCII碼表示,所以與中間的內容完全一樣。
0123 ,33323130 ,33323130 4567 ,37363534 ,37363534 89AB ,42413938 ,42413938 CDEF ,46454443 ,46454443 12 ,3231 ,3231
從這裡得到啟示,先把字符按4個字節複製,不足4字節則按位複製,這樣就會大大提高效率。這裡先使用對源字符串求長度的方法,為了完成複製字符串,需要同步移動指針p和p2。
【例16.14】演示按4個字節複製字符串的例子。
#include <stdio.h> void main ( ) { char s2[32]="0123456789ABCDEF12" ; char s1[32]=" " ; char *cp=s2 ; int *p ,*p2 ,i=0 ,len=0 ; while (*cp !='\0' ) ; { len++ ; cp++ ; } len++ ; printf ("%d\n" ,len ); if (len%4==0 ) len=len/4 ; else len=len/4+1 ; printf ("%d\n" ,len ); p= (int* )s2 ; p2= (int* )s1 ; for (i=0 ;i<len ;i++ ){ *p2=*p ; printf ("%s ,%x ,%x\n" ,(char* )p2 ,*p2 ,* (p+i )); p2++ ;p++ ; } printf (s1 ); printf ("\n" ); }
程序增加求字符串長度和循環次數的調試信息。複製輸出仍然分為3列,最後輸出複製給字符數組s1的內容。
19 5 0123 ,33323130 ,33323130 4567 ,37363534 ,42413938 89AB ,42413938 ,3231 CDEF ,46454443 ,0 12 ,3231 ,12ffc0 0123456789ABCDEF12
包含頭文件「string.h」就可使用庫函數strlen求字符串長度,即
len=strlen (s2 )+1 ;
如果設計一個宏,專門用來判斷是否是一個完整的4字節,如果是,則按4字節複製,否則按逐字節複製,這樣就可以簡化程序的設計,下面給出完整的例子。
【例16.15】使用宏來實現按4個字節複製字符串的參考程序。
#include <stdlib.h> #include <stdio.h> #define CONTAIN_OF_ZERO_BYTE (n ) \ (((n-0x01010101 ) & (~n )) & 0x80808080 ) char *mycopy (char *dest ,char *src ) { int len=dest-src ; int *d ,*s ; d= (int * )dest ; // 強制轉換成整數指針類型 s= (int * )src ; // 強制轉換成整數指針類型 if (dest == NULL || src == NULL ) return dest ; while (1 ) { if (!CONTAIN_OF_ZERO_BYTE (*s )) { printf ("*s%x " ,*s ); // 準備複製4 個字節的內容 printf ("%s\n" ,(char * )(s+1 )); // 尚沒有完成複製的字符串 *d=*s ; s++ ; d++ ; continue ; } src= (char * )s ; // 強制轉換回字符指針類型 printf ("*src%x %s\n" ,*src ,src+1 ); // 演示逐字節複製 while (* (src+len )=*src++ ) printf ("*src%x %s\n" ,*src ,src+1 ); // 演示逐字節複製 break ; } return dest ; } void main ( ) { char s2[32]="0123456789ABCDEF12" ; char s1[32]=" " ; mycopy (s1 ,s2 ); printf (s1 ); printf ("\n" ); }
程序中加入的輸出信息是為了幫助理解執行過程。選擇特殊的字符串也是為了能容易看出複製過程。
程序運行輸出如下:
*s33323130 0123456789ABCDEF12 *s37363534 456789ABCDEF12 *s42413938 89ABCDEF12 *s46454443 CDEF12 *src31 12 *src32 2 *src0 0123456789ABCDEF12
程序輸出的左邊是每次複製給目標數組的內容,右邊是尚沒有複製的內容。執行4次以後,剩下"12'\0'",改為一個一個地複製,共執行3次。
這類例子很多,著眼點都是減少循環的次數,不再贅述。