第一篇已经讨论了很多典型错误,可以供学习参考。本节主要列举一些典型问题。这里没有用错误一词,而改用问题,就是说设计的语句本身没有错误,但没有达到要求,或者虽然达到要求,但存在效率问题。
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次。
这类例子很多,着眼点都是减少循环的次数,不再赘述。