C语言中关于字符和字符串的操作是非常多的,操作字符和字符串往往就要用到字符串相关的函数。因此在这里我们就先来了解下函数库中的字符串函数,并且自己实现字符串函数。
strlen
size_t strlen ( const char * str );
字符串是以‘\0’作为结束标志,strlen函数是用于求字符串的字符长度(不包括‘\0’),它的返回值是字符串‘\0’前字符的个数。
strlen的模拟实现(my_strlen)
size_t my_strlen(const char* str){
assert(str != NULL);
size_t count = 0;
while(*str++ != '\0'){
count++;
}
return count;
}
strcpy
char* strcpy(char * destination, const char * source );
字符串拷贝需要两个字符串,一个为源字符串,一个为目标字符串,字符串拷贝只进行字符串拷贝,并且会将字符串的结束符也给拷贝进去,因此字符串拷贝时会有以下要点:
- 源字符必须是以‘\0’结束。
- 目标空间必须足够的大,可以容纳源字符。
- 源字符会将‘\0’拷贝进目标空间。
- 目标空间必须可见
举个实例:
char str1[100] = "abcdef";
char str2[100] = "ABCD";
strcpy(str1,str2);
printf("%s\n",str1;
//此时数组输出的是什么
如上例,按照正常来说,目标空间里应该存放的是”ABCD\0ef”,但最终输出的结果却是”ABCD”,这是为神魔呢。解决这个问题就要了解上面所说的字符串拷贝的一些要点了,字符串拷贝时将‘\0’也一同拷贝,从而将原字符串隔断了,因此就只显示了str2。
strcpy的实现(my_strcpy):
char* my_strcpy(char* dst,const char* src){
assert((dst != NULL)&&(src != NULL));
char* ret = dst;
while((*dst++ = *src++) != '\0';
retrun ret;
}
通过上面的实例我们可以看出,strcpy只能讲一个完整的字符串拷贝进另一字符串中,而且还会覆盖该字符串(这就有一种耍流氓的感觉),因此为了解决这一问题,我们就有了strncpy,它可以将源字符串的一部分复制到目标字符串中。
char *strncpy( char *strDest, const char *strSource, size_t count );
my_strncpy:
char* my_strncpy(char* dst,const char* src,size_t num){
assert((dst != NULL)&&(src != NULL));
char* ret = dst;
while(num-- && (*dst++ = *src++)!='\0');
return ret;
}
strcpy和strncpy的思路是相似的,但是strcpy在拷贝字符串的时候会将字符串的结束符‘\0’也拷贝到目标空间中,而strncpy不会将‘\0’拷贝进去(除了最后一个参数是sizeof(src)),因此可以说strcpy是字符串拷贝,而strncpy是字符拷贝。
strcat
char *strcat( char *strDestination, const char *strSource );
strcat是将源字符串拷贝到目标字符串之后,在拷贝时源字符串会覆盖掉目标字符串最后的‘\0’。要实现strcat需要具有以下几个要点:
- 源字符串必须以‘\0’结尾
- 目标字符串空间必须足够的大,要保证可以放下源字符串的所有内容
- 目标空间必须可以修改
strcat的模拟实现:
char* my_strcat(char* dst,const char* src){
assert((dst != NULL)&&(src != NULL));
while(*dst != '\0'){
dst++;
}
while((*dst++ = *src++) != '\0');
return dst;
}
strcat还有一个需要注意的问题:当dst为数组,src为指针时,如下例:
int main(void)
{
char dst[6] = "Hello";
char *src = "World";
strcat(dest, src);
printf("%s\n", dest);
return 0;
}
其输出结果为:HelloWorld,按理来说dst应该没有足够的空间来存储src,但却可以正常编译,且结果正确,很明显,这属于数组越界的问题,在C语言中,c不检查也不提示,所以这里的拷贝用到了dst[6]后面紧挨着的几个存储单元,因此如果dst和src反过来就是不可以的了。
strcat只是将src整体拷贝在dst后面(覆盖了‘\0’),但如果只想将src一部分拷贝到dst后面呢?这就引用了strncat。
char *strncat( char *strDest, const char *strSource, size_t count );
strncat的模拟实现:
char* my_strncat(char* dst, const char* src, size_t num){
assert((dst != NULL)&&(src != NULL));
while(*dst != '\0'){
dst++;
}
while(num--){
*dst++ = *src++;
}
return dst;
}
strcmp
int strcmp( const char *string1, const char *string2 );
strcmp其主要功能是比较两个字符串,它有以下要点:
- string1大于string2,则返回大于0的数,我们一般返回1。
- string1等于string2,返回0。
- string1小于string2,返回小于0的数,我们一般返回-1。
strcmp两个字符串比较的是其每个字符的ASCII值,比较是每个字符之间进行比较,依次往后比较,只要有大于就返回1,有小于就返回-1,只有当整个字符串比较结束时,才能判断两字符串是否相等。
strcmp模拟实现:
int my_strcmp(char* str1,const char* str2){
assert((str1 != NULL)&&(str2 != NULL));
while((*str1 != '\0')||(*str2 != '\0')){
if(*str1 > *str2){
return 1;
}
if(*str1 < *str2){
return -1;
}
str1++;
str2++;
}
return 0;
}
strcmp只满足与两个完整的字符串相比较,因此当我们需要比较两个字符串的前n个字符的时候,我们就要引用strncmp函数。
int strncmp( const char *string1, const char *string2, size_t count );
strncmp的模拟实现:
int my_strncmp(const char* str1, const char* str2, size_t num){
assert((str1 != NULL)&&(str2 != NULL));
while(num--){
if(*str1 > *str2){
return 1;
}
if(*str1 < *str2){
return -1;
}
str1++;
str2++;
}
return 0;
}