无知乱吃药
据说,联合国曾提出过一个口号:“不要死于无知”。无知的一种情况就是只知道“对症下药”,却不知道很多药物都有各自的禁忌,比如孕妇忌用,高血压忌服等等。不顾禁忌乱吃药,即使对症也会造成对身体的伤害。
写代码也是如此,仅仅知道函数的功能是远远不够的,还必须知道什么情况下函数在什么情况下能使用什么情况下不能使用。比如说,你不能用sqrt()函数去求一个负数的平方根。下面的代码中,就存在着类似的错误。
#include <stdio.h>
#include <string.h>
#define N 10
int main( void )
{/*……*/
void sort(int num[],char name[][8]);
int num[N]
char name[N][8];
/*……*/
sort(num,name);
/*……*/
return 0;
}
void sort(int num[],char name[N][8])
{int i,j,min,temp1;
char temp2[8];
for(i=0;i<N-1;i++)
{min=i;
for(j=i;j<N;j++)
if(num[min]>num[j])min=j;
temp2=num[i];
strcpy(temp2,name[i]);
num[i]=num[min];
strcpy(name[i],name[min]);
num[min]=temp1;
strcpy(name[min],temp2);
}
printf("\n result:\n");
for(i=0;i<N;i++)
printf("\n %5d%10s",num[i],name[i]);
}
——谭浩强 ,《C程序设计(第四版)学习辅导》,清华大学出版社,2010年7月,p92
这段代码的毛病很多。包括sort()函数夹带输出功能,sort()函数参数不完整等等等等。为了便于阅读查找错误,首先把这些极为刺眼的毛病改掉。
#include <stdio.h>
#include <string.h>
#define N 10
void sort (int num[],char name[][8],int);
void output(int num[],char name[][8],int);
int main( void )
{
int num[N]
char name[N][8];
/*……*/
sort(num,name,N);
output(num,name,N);
/*……*/
return 0;
}
void sort(int num[],char name[][8],int size)
{
int i,j,min,temp1;
char temp2[8];
for( i = 0 ; i < size-1 ; i++)
{
min=i;
for( j = i ; j< size ; j++ )
if(num[min]>num[j])
min=j;
temp2=num[i];
num[i]=num[min];
num[min]=temp1;
strcpy(temp2,name[i]);
strcpy(name[i],name[min]);
strcpy(name[min],temp2);
}
}
修改后的代码中依然保留着原来就有的错误。这个错误就是
strcpy(name[i],name[min]);
这条语句。
为什么说这条语句是错误的呢?因为从代码中不难看到,min可能与i的值相同,在min与i值相等的情况下,就成了字符串向自身拷贝自身,这时strcpy()函数调用就是一个错误的调用,它违背了使用strcpy()函数的禁忌——源字符串不可与目标字符串重叠。
在C89中对strcpy()函数是这样描述的:
char *strcpy(char *sl, const char *s2);
strcpy()函数拷贝s2所指向的字符串(含结尾的null字符)至s1指向的数组。如果两个对象重叠(overlap),函数行为是未定义的(undefined)。
未定义行为的代码就是一种错误的代码,也就是说使用strcpy()函数的前提条件是目标字符串与源字符串之间不可以有重叠,否则代码就是错误的代码。在两个对象存在重叠时使用strcpy()就和无知地不顾药物禁忌乱吃药一样荒唐。
在C99中,用了一个新的关键字——restrict来明确地表明strcpy()函数的使用前提条件是不容许重叠:
char *strcpy(char * restrict s1,const char * restrict s2);
这里的restrict的含义是s1和s2这两个指针是访问它们所指向的对象的唯一途径或手段。
如果两个字符串有重叠,应该使用memmove()函数而不能使用strcpy()函数。当然,就前面的代码而言,不使用memmove()函数也可以避免这个错误。只需将sort()做如下改动即可:
void sort(int num[],char name[][8],int size) { int i,j,min,temp1; char temp2[8]; for( i = 0 ; i < size-1 ; i++) { min=i; for( j = i ; j< size ; j++ ) if(num[min]>num[j]) min=j; if( min != i ) { temp2=num[i]; num[i]=num[min]; num[min]=temp1; strcpy(temp2,name[i]); strcpy(name[i],name[min]); strcpy(name[min],temp2); } } }