题目:
实现库函数strncpy、strncat和strncmp,它们最多对参数字符串中的前n个字符进行操作。例如,函数strccpy(s, t, n)将t中最多n个字符复制到s中。更详细的说明详见附录B。
自我解答:
对于strncpy的实现
编程思路:把字符串t中的前n个字符拷贝到s中,如果t的字符数小于n,则拷贝到\0为止。
void strncpy(char *s, char *t, int n)
{
while(n--)
if(*t)
*s++ = *t++;
*s = '\0';
}
参照附录B,strncat(s, t, n)实现的是 将字符串t中最多n个字符连接到字符串s的尾部,并返回s。
由于函数返回的是s,所以在strncat内部需要先把s的值进行保存;然后找到s的末尾把t进行复制。
char *strncat(char *s, char *t, int n)
{
char *bs = s;
while(*s)
s++;
while(n--)
if(*t)
*s++ = *t++;
return bs;
}
参照附录B,strncmp(s, t ,n)实现的是将字符串s中最多前n个字符与字符串t相比较。当s < t时,返回一个负数,当s > t时,返回一个正数。
int strncmp(char *s, char *t, int n)
{
for(; (*s == *t) && n > 0; n--, s++, t++)
{
if(*s == '\0' || *t == '\0')
break;
}
if(n == 0)
return 0;
else
return *s - *t;
}
在for循环中增加对s和t结尾的判断是为了防止 指针s和t溢出,跳出循环后,如果n等于0则说明s和t的前n个字符完全匹配,否则就是不匹配的情况,返回此时不匹配字符的ASCII码差值。
参考答案:
/* strncpy: copy n characters from t to s */
void strncpy(char *s, char *t, int n)
{
while(*t && n-- > 0)
*s++ = *t++;
while(n-- > 0)
*s++ = '\0';
}
/* strncat: concatenate n characters of t to the end of s */
void strncat(char *s, char *t, int n)
{
void strncpy(char *s, char *t, int n);
int strlen(char *);
strncpy(s + strlen(s), t, n);
}
/* strncmp: compare at most n characters of t with s */
int strncmp(char *s, char *t, int n)
{
for(; *s == *t; s++, t++)
if(*s == '\0' || --n <= 0)
return 0;
return *s - *t;
}
函数strncpy和strncat的类型都是void,与教材第91页上的strcpy一样。这几个函数的库函数版本返回的都是一个指向结果字符串开头的指针。
strncpy函数从字符串t最多复制n个字符到字符串s。如果t中的字符少于n,我们将在字符串s的末尾填充'\0'字符。
strncat函数将调用strncpy函数把字符串t的前n个字符追加到字符串s的末尾。
strncmp将对字符串t和s的前n个字符进行比较。strncmp与教材第92页上的strcmp函数很相似,只是这次我们将在到达字符串s或t的末尾或者成功地比较了n个字符后终止比较操作,如下所示:
if(*s == '\0' || --n <= 0)
return 0;
补充:
1. 关于strncpy函数,在t的字符个数小于n时,自我解答和参考答案实现效果类似,即把t中的所有字符都拷贝到s中,一个区别是自我解答在拷贝完成之后向s的末尾添加了\0字符,而参考答案中添加了n-strlen(t)个'\0字符。
另一种情况是当t的字符数大于n时,自我解答中在把t中的前n个字符拷贝到s之后,在s的结尾添加了'\0'字符,而参考答案中没有在s的末尾添加\0字符。参考了string.h库中的strncpy函数,在这种情况下和参考答案中的结果一致。
所以自我解答相当于是字符串的精确拷贝,参考答案和string库中的函数相当于是替换。
自我解答中的代码可以进一步精简为
void strncpy(char *s, char *t, int n)
{
while(n-- && *t)
*s++ = *t++;
*s = '\0';
}
2. 关于strncat,自我解答时参考了标准库中的返回值,即返回字符串s的初始位置。在拷贝结束之后并没有在s的末尾添加'\0',所以在有些情况并不能得到正确的输入,例如下面测试程序
#include <stdio.h>
char *strncat(char *s, char *t, int n);
int main()
{
char str1[30] = "s";
char str2[30] = "hello world";
str1[3] = 'd';
printf("%s\n", strncat(str1, str2, 2));
}
char *strncat(char *s, char *t, int n)
{
char *bs = s;
while(*s)
s++;
while(n--)
if(*t)
*s++ = *t++;
return bs;
}
输出的结果是"shed",在调用strncat时,在s之后拷贝了sh两个字符,但是没有在最后添加'\0',所以导致拷贝之后的字符串会有字符的冗余。在使用string.h中strncat函数输出结果是 “she”,改进方式是在return bs之前,增加 *s = '\0'的语句。
参考答案中的实现借用了其实现的strncpy函数和标准库中的strlen函数,由于strncpy函数并没有实现末尾增加'\0'的功能,所以也会有字符冗余的可能。
3. 对于strncmp的实现,参考答案更为简练。注意if中的语句:
if(*s == '\0' || --n <= 0) 只需要*s == '\0'即可,不需要再判断*t是否等于'\0', 因为能执行到这里已经满足了for中的循环条件:*s == *t。return 0的条件要么是s和t都到了末尾,要么是n等于了0(小于0是防止n等于0的情况)。